Android API कॉल में आम तौर पर, हर इनवोकेशन के लिए ज़्यादा लेटेन्सी और कंप्यूटेशन शामिल होता है. इसलिए, क्लाइंट-साइड कैश मेमोरी का इस्तेमाल करना, ऐसे एपीआई डिज़ाइन करने के लिए ज़रूरी है जो मददगार, सही, और बेहतर परफ़ॉर्म करते हों.
वजह
Android SDK में ऐप्लिकेशन डेवलपर के लिए उपलब्ध कराए गए एपीआई को अक्सर Android फ़्रेमवर्क में क्लाइंट कोड के तौर पर लागू किया जाता है. यह क्लाइंट कोड, प्लैटफ़ॉर्म प्रोसेस में मौजूद सिस्टम सेवा को बाइंडर आईपीसी कॉल करता है. इस सिस्टम सेवा का काम, कुछ कैलकुलेशन करना और क्लाइंट को नतीजा देना होता है. इस कार्रवाई में लगने वाला समय आम तौर पर इन तीन बातों पर निर्भर करता है:
- आईपीसी ओवरहेड: आम तौर पर, एक बुनियादी आईपीसी कॉल में लगने वाला समय, एक बुनियादी इन-प्रोसेस तरीके के कॉल में लगने वाले समय से 10,000 गुना ज़्यादा होता है.
 - सर्वर-साइड कंटेंशन: क्लाइंट के अनुरोध के जवाब में सिस्टम सेवा में किया गया काम तुरंत शुरू नहीं हो सकता. उदाहरण के लिए, अगर कोई सर्वर थ्रेड, पहले से मिले अन्य अनुरोधों को प्रोसेस करने में व्यस्त है.
 - सर्वर-साइड पर कंप्यूटेशन: सर्वर पर अनुरोध को हैंडल करने के लिए, काफ़ी काम करना पड़ सकता है.
 
क्लाइंट-साइड पर कैश मेमोरी लागू करके, इंतज़ार के समय को बढ़ाने वाले इन तीनों फ़ैक्टर को कम किया जा सकता है. हालांकि, इसके लिए ज़रूरी है कि कैश मेमोरी:
- सही: क्लाइंट-साइड कैश कभी ऐसे नतीजे नहीं दिखाता जो सर्वर से मिले नतीजों से अलग हों.
 - असरदार: क्लाइंट के अनुरोधों को अक्सर कैश मेमोरी से पूरा किया जाता है. उदाहरण के लिए, कैश मेमोरी का हिट रेट ज़्यादा होता है.
 - असरदार: क्लाइंट-साइड कैश मेमोरी, क्लाइंट-साइड के संसाधनों का असरदार तरीके से इस्तेमाल करती है. जैसे, कैश मेमोरी में सेव किए गए डेटा को छोटे तरीके से दिखाना. साथ ही, क्लाइंट की मेमोरी में कैश मेमोरी में सेव किए गए बहुत ज़्यादा नतीजे या पुराना डेटा सेव न करना.
 
क्लाइंट में सर्वर के नतीजों को कैश मेमोरी में सेव करें
अगर क्लाइंट अक्सर एक ही अनुरोध को कई बार करते हैं और समय के साथ जवाब में कोई बदलाव नहीं होता है, तो आपको क्लाइंट लाइब्रेरी में एक कैश मेमोरी लागू करनी चाहिए. इसे अनुरोध के पैरामीटर के हिसाब से सेट किया जाता है.
लागू करने के लिए, IpcDataCache का इस्तेमाल करें:
public class BirthdayManager {
    private final IpcDataCache.QueryHandler<User, Birthday> mBirthdayQuery =
            new IpcDataCache.QueryHandler<User, Birthday>() {
                @Override
                public Birthday apply(User user) {
                    return mService.getBirthday(user);
                }
            };
    private static final int BDAY_CACHE_MAX = 8;  // Maximum birthdays to cache
    private static final String BDAY_API = "getUserBirthday";
    private final IpcDataCache<User, Birthday> mCache
            new IpcDataCache<User, Birthday>(
                BDAY_CACHE_MAX, MODULE_SYSTEM, BDAY_API,  BDAY_API, mBirthdayQuery);
    /** @hide **/
    @VisibleForTesting
    public static void clearCache() {
        IpcDataCache.invalidateCache(MODULE_SYSTEM, BDAY_API);
    }
    public Birthday getBirthday(User user) {
        return mCache.query(user);
    }
}
पूरे उदाहरण के लिए, android.app.admin.DevicePolicyManager देखें.
IpcDataCache सभी सिस्टम कोड के लिए उपलब्ध है. इसमें मेनलाइन मॉड्यूल भी शामिल हैं.
इसके अलावा, PropertyInvalidatedCache भी है. यह लगभग एक जैसा है, लेकिन सिर्फ़ फ़्रेमवर्क को दिखता है. जब भी मुमकिन हो, IpcDataCache का इस्तेमाल करें.
सर्वर साइड में किए गए बदलावों के आधार पर कैश मेमोरी को अमान्य करना
अगर सर्वर से मिली वैल्यू समय के साथ बदल सकती है, तो बदलावों को मॉनिटर करने के लिए कॉलबैक लागू करें. साथ ही, कॉलबैक रजिस्टर करें, ताकि क्लाइंट-साइड कैश मेमोरी को उसके हिसाब से अमान्य किया जा सके.
यूनिट टेस्ट केस के बीच कैश मेमोरी को अमान्य करना
यूनिट टेस्ट सुइट में, क्लाइंट कोड की जांच असली सर्वर के बजाय टेस्ट डबल के ख़िलाफ़ की जा सकती है. अगर हां, तो टेस्ट केस के बीच क्लाइंट-साइड की सभी कैश मेमोरी मिटाएं. ऐसा इसलिए किया जाता है, ताकि टेस्ट केस एक-दूसरे से अलग रहें और एक टेस्ट केस दूसरे टेस्ट केस में रुकावट न डाले.
@RunWith(AndroidJUnit4.class)
public class BirthdayManagerTest {
    @Before
    public void setUp() {
        BirthdayManager.clearCache();
    }
    @After
    public void tearDown() {
        BirthdayManager.clearCache();
    }
    ...
}
जब ऐसे एपीआई क्लाइंट के लिए सीटीएस टेस्ट लिखे जाते हैं जो इंटरनल तौर पर कैश मेमोरी का इस्तेमाल करता है, तो कैश मेमोरी को लागू करने की जानकारी एपीआई डेवलपर को नहीं दी जाती. इसलिए, सीटीएस टेस्ट के लिए क्लाइंट कोड में इस्तेमाल की गई कैश मेमोरी के बारे में कोई खास जानकारी नहीं होनी चाहिए.
कैश हिट और मिस की स्टडी करना
IpcDataCache और PropertyInvalidatedCache लाइव आंकड़े प्रिंट कर सकते हैं:
adb shell dumpsys cacheinfo
  ...
  Cache Name: cache_key.is_compat_change_enabled
    Property: cache_key.is_compat_change_enabled
    Hits: 1301458, Misses: 21387, Skips: 0, Clears: 39
    Skip-corked: 0, Skip-unset: 0, Skip-bypass: 0, Skip-other: 0
    Nonce: 0x856e911694198091, Invalidates: 72, CorkedInvalidates: 0
    Current Size: 1254, Max Size: 2048, HW Mark: 2049, Overflows: 310
    Enabled: true
  ...
फ़ील्ड्स की फ़िल्में
Hits:
- डेफ़िनिशन: यह इस बात की जानकारी देता है कि अनुरोध किए गए डेटा को कितनी बार कैश मेमोरी में खोजा गया.
 - अहमियत: इससे पता चलता है कि डेटा को तेज़ी से और आसानी से ऐक्सेस किया जा सकता है. साथ ही, इससे गैर-ज़रूरी डेटा को ऐक्सेस करने की ज़रूरत नहीं पड़ती.
 - आम तौर पर, ज़्यादा संख्या बेहतर होती है.
 
इन कुकी का डेटा मिटाता है:
- डेफ़िनिशन: अमान्य होने की वजह से, कैश मेमोरी को कितनी बार मिटाया गया.
 - डेटा मिटाने की वजहें:
- अमान्य होना: सर्वर से मिला डेटा पुराना है.
 - स्टोरेज मैनेजमेंट: कैश मेमोरी भर जाने पर, नए डेटा के लिए जगह बनाना.
 
 - ज़्यादा संख्या का मतलब है कि डेटा में बार-बार बदलाव हो रहा है और इससे परफ़ॉर्मेंस पर असर पड़ सकता है.
 
मिलान नहीं हुआ:
- डेफ़िनिशन: यह कुकी बताती है कि कैश मेमोरी में सेव किए गए डेटा को कितनी बार ऐक्सेस नहीं किया जा सका.
 - वजह:
- कैश मेमोरी का सही तरीके से इस्तेमाल न होना: कैश मेमोरी बहुत छोटी है या सही डेटा सेव नहीं कर रही है.
 - बार-बार बदलने वाला डेटा.
 - पहली बार किए गए अनुरोध.
 
 - ज़्यादा संख्या का मतलब है कि कैश मेमोरी से जुड़ी समस्याएं हो सकती हैं.
 
स्किप किए गए वीडियो:
- परिभाषा: ऐसे मामले जहां कैश मेमोरी का इस्तेमाल नहीं किया गया, जबकि इसका इस्तेमाल किया जा सकता था.
 - स्किप करने की वजहें:
- कॉर्किंग: यह Android Package Manager के अपडेट से जुड़ी समस्या है. बूटिंग के दौरान ज़्यादा कॉल आने की वजह से, जान-बूझकर कैश मेमोरी को बंद कर दिया जाता है.
 - अनसेट: कैश मेमोरी मौजूद है, लेकिन शुरू नहीं की गई है. नॉन्स सेट नहीं किया गया था. इसका मतलब है कि कैश मेमोरी को कभी अमान्य नहीं किया गया.
 - बाइपास: कैश मेमोरी को जान-बूझकर स्किप करने का फ़ैसला.
 
 - ज़्यादा संख्या से पता चलता है कि कैश मेमोरी का इस्तेमाल सही तरीके से नहीं किया जा रहा है.
 
अमान्य करता है:
- डेफ़िनिशन: कैश मेमोरी में सेव किए गए डेटा को पुराना या बासी के तौर पर मार्क करने की प्रोसेस.
 - अहमियत: इससे यह सिग्नल मिलता है कि सिस्टम सबसे नए डेटा के साथ काम करता है. इससे गड़बड़ियों और डेटा में अंतर को रोकने में मदद मिलती है.
 - आम तौर पर, यह कुकी उस सर्वर से ट्रिगर होती है जिसके पास डेटा का मालिकाना हक होता है.
 
मौजूदा साइज़:
- डेफ़िनिशन: कैश मेमोरी में मौजूद एलिमेंट की मौजूदा संख्या.
 - अहमियत: इससे पता चलता है कि कैश मेमोरी में मौजूद संसाधनों का इस्तेमाल कैसे किया जा रहा है और सिस्टम की परफ़ॉर्मेंस पर इसका क्या असर पड़ सकता है.
 - ज़्यादा वैल्यू का मतलब आम तौर पर यह होता है कि कैश मेमोरी का इस्तेमाल ज़्यादा किया जा रहा है.
 
ज़्यादा से ज़्यादा साइज़:
- परिभाषा: कैश मेमोरी के लिए तय की गई ज़्यादा से ज़्यादा जगह.
 - अहमियत: इससे कैश मेमोरी की क्षमता और डेटा सेव करने की क्षमता का पता चलता है.
 - ज़्यादा से ज़्यादा साइज़ सेट करने से, मेमोरी के इस्तेमाल के साथ-साथ कैश मेमोरी की परफ़ॉर्मेंस को बेहतर बनाने में मदद मिलती है. ज़्यादा से ज़्यादा साइज़ तक पहुंचने के बाद, सबसे कम इस्तेमाल किए गए एलिमेंट को हटाकर एक नया एलिमेंट जोड़ा जाता है. इससे पता चलता है कि कैश मेमोरी का इस्तेमाल सही तरीके से नहीं किया जा रहा है.
 
हाई वॉटर मार्क:
- डेफ़िनिशन: यह कैश मेमोरी के बनने के बाद से, उसके साइज़ की ज़्यादा से ज़्यादा सीमा होती है.
 - अहमियत: इससे कैश मेमोरी के सबसे ज़्यादा इस्तेमाल और मेमोरी पर पड़ने वाले संभावित दबाव के बारे में जानकारी मिलती है.
 - हाई वॉटर मार्क पर नज़र रखने से, संभावित रुकावटों या ऑप्टिमाइज़ेशन के क्षेत्रों की पहचान करने में मदद मिल सकती है.
 
ओवरफ़्लो:
- डेफ़िनिशन: यह मेट्रिक, कैश मेमोरी के तय किए गए साइज़ से ज़्यादा होने की संख्या दिखाती है. साथ ही, यह भी दिखाती है कि नई एंट्री के लिए जगह बनाने के लिए, कैश मेमोरी से डेटा को कितनी बार हटाना पड़ा.
 - अहमियत: इससे कैश मेमोरी पर पड़ने वाले दबाव और डेटा को हटाने की वजह से परफ़ॉर्मेंस में संभावित गिरावट के बारे में पता चलता है.
 - ओवरफ़्लो की ज़्यादा संख्या से पता चलता है कि कैश मेमोरी के साइज़ में बदलाव करने या कैश मेमोरी की रणनीति का फिर से आकलन करने की ज़रूरत हो सकती है.
 
ये आंकड़े, गड़बड़ी की रिपोर्ट में भी देखे जा सकते हैं.
कैश मेमोरी के साइज़ को ज़रूरत के हिसाब से सेट करना
कैश मेमोरी का साइज़ तय होता है. कैश मेमोरी का साइज़ तय सीमा से ज़्यादा होने पर, LRU क्रम में एंट्री हटा दी जाती हैं.
- बहुत कम एंट्री को कैश मेमोरी में सेव करने से, कैश मेमोरी हिट रेट पर बुरा असर पड़ सकता है.
 - बहुत ज़्यादा एंट्री को कैश मेमोरी में सेव करने से, कैश मेमोरी के इस्तेमाल की सीमा बढ़ जाती है.
 
अपनी ज़रूरत के हिसाब से सही बैलेंस बनाएं.
गैर-ज़रूरी क्लाइंट कॉल हटाएं
क्लाइंट, सर्वर से एक ही क्वेरी को कम समय में कई बार कर सकते हैं:
public void executeAll(List<Operation> operations) throws SecurityException {
    for (Operation op : operations) {
        for (Permission permission : op.requiredPermissions()) {
            if (!permissionChecker.checkPermission(permission, ...)) {
                throw new SecurityException("Missing permission " + permission);
            }
        }
        op.execute();
  }
}
पिछले कॉल के नतीजों का फिर से इस्तेमाल करने के बारे में सोचें:
public void executeAll(List<Operation> operations) throws SecurityException {
    Set<Permission> permissionsChecked = new HashSet<>();
    for (Operation op : operations) {
        for (Permission permission : op.requiredPermissions()) {
            if (!permissionsChecked.add(permission)) {
                if (!permissionChecker.checkPermission(permission, ...)) {
                    throw new SecurityException(
                            "Missing permission " + permission);
                }
            }
        }
        op.execute();
  }
}
हाल ही के सर्वर रिस्पॉन्स के क्लाइंट-साइड मेमोराइज़ेशन का इस्तेमाल करें
क्लाइंट ऐप्लिकेशन, एपीआई से ज़्यादा तेज़ी से क्वेरी कर सकते हैं. हालांकि, एपीआई का सर्वर इतने कम समय में नए और काम के जवाब जनरेट नहीं कर सकता. इस मामले में, क्लाइंट-साइड पर आखिरी बार देखे गए सर्वर रिस्पॉन्स को टाइमस्टैंप के साथ मेमोइज़ करना एक असरदार तरीका है. साथ ही, अगर मेमोइज़ किया गया नतीजा हाल ही का है, तो सर्वर से क्वेरी किए बिना मेमोइज़ किया गया नतीजा दिखाना चाहिए. एपीआई क्लाइंट का डेवलपर, मेमोराइज़ेशन की अवधि तय कर सकता है.
उदाहरण के लिए, कोई ऐप्लिकेशन हर फ़्रेम में आंकड़ों के लिए क्वेरी करके, उपयोगकर्ता को नेटवर्क ट्रैफ़िक के आंकड़े दिखा सकता है:
@UiThread
private void setStats() {
    mobileRxBytesTextView.setText(
        Long.toString(TrafficStats.getMobileRxBytes()));
    mobileRxPacketsTextView.setText(
        Long.toString(TrafficStats.getMobileRxPackages()));
    mobileTxBytesTextView.setText(
        Long.toString(TrafficStats.getMobileTxBytes()));
    mobileTxPacketsTextView.setText(
        Long.toString(TrafficStats.getMobileTxPackages()));
}
ऐप्लिकेशन, 60 हर्ट्ज़ पर फ़्रेम बना सकता है. हालांकि, TrafficStats में मौजूद क्लाइंट कोड, हर सेकंड में ज़्यादा से ज़्यादा एक बार सर्वर से आंकड़ों के लिए क्वेरी कर सकता है. अगर पिछली क्वेरी के एक सेकंड के अंदर क्वेरी की जाती है, तो वह पिछली बार देखी गई वैल्यू दिखाएगा.
इसकी अनुमति है, क्योंकि एपीआई के दस्तावेज़ में, दिखाए गए नतीजों के अपडेट होने के बारे में कोई कानूनी समझौता नहीं किया गया है.
participant App code as app
participant Client library as clib
participant Server as server
app->clib: request @ T=100ms
clib->server: request
server->clib: response 1
clib->app: response 1
app->clib: request @ T=200ms
clib->app: response 1
app->clib: request @ T=300ms
clib->app: response 1
app->clib: request @ T=2000ms
clib->server: request
server->clib: response 2
clib->app: response 2
सर्वर क्वेरी के बजाय, क्लाइंट-साइड कोड जनरेशन का इस्तेमाल करें
अगर क्वेरी के नतीजे, बिल्ड टाइम पर सर्वर को पता होते हैं, तो यह देखें कि क्या बिल्ड टाइम पर क्लाइंट को भी उनके बारे में पता होता है. साथ ही, यह देखें कि क्या एपीआई को पूरी तरह से क्लाइंट साइड पर लागू किया जा सकता है.
यहां दिए गए ऐप्लिकेशन कोड पर ध्यान दें. यह कोड जांच करता है कि डिवाइस कोई स्मार्टवॉच है या नहीं. इसका मतलब है कि डिवाइस पर Wear OS चल रहा है या नहीं:
public boolean isWatch(Context ctx) {
    PackageManager pm = ctx.getPackageManager();
    return pm.hasSystemFeature(PackageManager.FEATURE_WATCH);
}
डिवाइस की इस प्रॉपर्टी के बारे में, बिल्ड टाइम पर पता चलता है. खास तौर पर, उस समय जब इस डिवाइस की बूट इमेज के लिए फ़्रेमवर्क बनाया गया था. hasSystemFeature के लिए क्लाइंट-साइड कोड, रिमोट PackageManager सिस्टम सेवा से क्वेरी करने के बजाय, तुरंत कोई जाना-पहचाना नतीजा दिखा सकता है.
क्लाइंट में सर्वर के कॉल बैक को डुप्लीकेट होने से रोकना
आखिर में, एपीआई क्लाइंट, एपीआई सर्वर के साथ कॉलबैक रजिस्टर कर सकता है, ताकि उसे इवेंट की सूचना मिल सके.
आम तौर पर, ऐप्लिकेशन एक ही जानकारी के लिए कई कॉलबैक रजिस्टर करते हैं. आईपीसी का इस्तेमाल करके, सर्वर को हर रजिस्टर किए गए कॉलबैक के लिए क्लाइंट को एक बार सूचना देने के बजाय, क्लाइंट लाइब्रेरी में आईपीसी का इस्तेमाल करके सर्वर के साथ एक रजिस्टर किया गया कॉलबैक होना चाहिए. इसके बाद, ऐप्लिकेशन में हर रजिस्टर किए गए कॉलबैक को सूचना देनी चाहिए.
digraph d_front_back {
  rankdir=RL;
  node [style=filled, shape="rectangle", fontcolor="white" fontname="Roboto"]
  server->clib
  clib->c1;
  clib->c2;
  clib->c3;
  subgraph cluster_client {
    graph [style="dashed", label="Client app process"];
    c1 [label="my.app.FirstCallback" color="#4285F4"];
    c2 [label="my.app.SecondCallback" color="#4285F4"];
    c3 [label="my.app.ThirdCallback" color="#4285F4"];
    clib [label="android.app.FooManager" color="#F4B400"];
  }
  subgraph cluster_server {
    graph [style="dashed", label="Server process"];
    server [label="com.android.server.FooManagerService" color="#0F9D58"];
  }
}