Android API çağrıları genellikle önemli gecikme ve çağrı başına hesaplama içerir. Bu nedenle, istemci tarafı önbelleğe alma, faydalı, doğru ve yüksek performanslı API'ler tasarlarken önemli bir husustur.
Motivasyon
Android SDK'da uygulama geliştiricilere sunulan API'ler genellikle Android Framework'te istemci kodu olarak uygulanır. Bu kod, bir platform sürecindeki sistem hizmetine Binder IPC çağrısı yapar. Bu hizmetin görevi, bazı hesaplamalar yapmak ve istemciye bir sonuç döndürmektir. Bu işlemin gecikme süresi genellikle üç faktörden etkilenir:
- IPC ek yükü: Temel bir IPC çağrısı,genellikle temel bir işlem içi yöntem çağrısının gecikme süresinin 10.000 katıdır.
 - Sunucu tarafında çekişme: İstemcinin isteğine yanıt olarak sistem hizmetinde yapılan çalışma hemen başlamayabilir. Örneğin, bir sunucu iş parçacığı daha önce gelen diğer istekleri işlemekle meşgulse.
 - Sunucu tarafı hesaplama: İsteğin sunucuda işlenmesi için gereken çalışma önemli bir iş yükü gerektirebilir.
 
Önbelleğin aşağıdaki koşulları karşılaması durumunda, istemci tarafında önbellek uygulayarak bu üç gecikme faktörünü de ortadan kaldırabilirsiniz:
- Doğru: İstemci tarafı önbelleği, sunucunun döndüreceğinden farklı sonuçlar döndürmez.
 - Etkili: İstemci istekleri genellikle önbellekten sunulur. Örneğin, önbelleğin yüksek bir isabet oranı vardır.
 - Verimli: İstemci tarafı önbelleği, önbelleğe alınmış verileri kompakt bir şekilde temsil ederek ve istemcinin belleğinde çok fazla önbelleğe alınmış sonuç veya eski veri depolamayarak istemci tarafı kaynaklarını verimli bir şekilde kullanır.
 
Sunucu sonuçlarını istemcide önbelleğe almayı düşünün.
Müşteriler genellikle aynı isteği birden çok kez gönderiyorsa ve döndürülen değer zaman içinde değişmiyorsa istemci kitaplığında istek parametreleriyle anahtarlanmış bir önbellek uygulamanız gerekir.
Uygulamanızda IpcDataCache kullanmayı deneyin:
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);
    }
}
Tam bir örnek için android.app.admin.DevicePolicyManager bölümüne bakın.
IpcDataCache, ana hat modülleri de dahil olmak üzere tüm sistem kodlarında kullanılabilir.
Ayrıca, neredeyse aynı olan ancak yalnızca çerçeve tarafından görülebilen PropertyInvalidatedCache da vardır. Mümkün olduğunda IpcDataCache tercih edin.
Sunucu tarafı değişikliklerde önbellekleri geçersiz kılma
Sunucudan döndürülen değer zaman içinde değişebiliyorsa değişiklikleri gözlemlemek için bir geri çağırma işlevi uygulayın ve istemci tarafı önbelleğini buna göre geçersiz kılabilmek için bir geri çağırma işlevi kaydedin.
Birim test durumları arasındaki önbellekleri geçersiz kılma
Bir birim testi paketinde, istemci kodunu gerçek sunucuya karşı değil, test çiftine karşı test edebilirsiniz. Bu durumda, test senaryoları arasında istemci tarafındaki önbellekleri temizlediğinizden emin olun. Bunun nedeni, test durumlarının karşılıklı olarak hermetik olmasını sağlamak ve bir test durumunun diğerine müdahale etmesini önlemektir.
@RunWith(AndroidJUnit4.class)
public class BirthdayManagerTest {
    @Before
    public void setUp() {
        BirthdayManager.clearCache();
    }
    @After
    public void tearDown() {
        BirthdayManager.clearCache();
    }
    ...
}
Dahili olarak önbelleğe alma özelliğini kullanan bir API istemcisini çalıştıran CTS testleri yazarken önbellek, API yazarının erişemediği bir uygulama ayrıntısıdır. Bu nedenle, CTS testleri istemci kodunda kullanılan önbelleğe alma hakkında özel bir bilgi gerektirmemelidir.
Önbellek isabetlerini ve kaçırılanlarını inceleme
IpcDataCache ve PropertyInvalidatedCache, canlı istatistikleri yazdırabilir:
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
  ...
Fields'ın oynadığı filmler
İstekler:
- Tanım: İstenen bir veri parçasının önbellekte başarıyla bulunduğu sayı.
 - Önem: Gereksiz veri alımını azaltarak verilerin verimli ve hızlı bir şekilde alındığını gösterir.
 - Daha yüksek sayılar genellikle daha iyidir.
 
Clears: (Temizlemeler)
- Tanım: Geçersiz kılma nedeniyle önbelleğin temizlenme sayısı.
 - Temizleme Nedenleri:
- Geçersiz kılma: Sunucudaki veriler güncel değil.
 - Alan yönetimi: Önbellek dolduğunda yeni veriler için yer açma
 
 - Yüksek sayı, sık değişen verileri ve olası verimsizliği gösterebilir.
 
Kaçanlar:
- Tanım: Önbelleğin istenen verileri sağlayamadığı sayı.
 - Nedenleri:
- Verimsiz önbelleğe alma: Önbellek çok küçük veya doğru verileri depolamıyor.
 - Sık değişen veriler
 - İlk kez yapılan istekler
 
 - Yüksek sayı, olası önbelleğe alma sorunlarına işaret eder.
 
Atlamalar:
- Tanım: Önbelleğin kullanılabileceği durumlarda hiç kullanılmaması.
 - Atlama nedenleri:
- Tıkanma: Android Paket Yöneticisi güncellemelerine özgü olarak, başlatma sırasında çok sayıda arama yapıldığından önbelleğe alma işleminin kasıtlı olarak devre dışı bırakılması.
 - Ayarlanmamış: Önbellek var ancak başlatılmamış. Nonce ayarlanmamış. Bu nedenle önbellek hiçbir zaman geçersiz kılınmamış.
 - Atlama: Önbelleği atlama kararı.
 
 - Yüksek sayılar, önbellek kullanımında olası verimsizlikleri gösterir.
 
Geçersiz kılan:
- Tanım: Önbelleğe alınan verilerin eski veya güncel olmayan olarak işaretlenmesi süreci.
 - Önemi: Sistemlerin en güncel verilerle çalıştığına dair bir sinyal vererek hataları ve tutarsızlıkları önler.
 - Genellikle verilerin sahibi olan sunucu tarafından tetiklenir.
 
Mevcut Boyut:
- Tanım: Önbellekteki mevcut öğe miktarı.
 - Önem: Önbelleğin kaynak kullanımını ve sistem performansı üzerindeki olası etkisini gösterir.
 - Daha yüksek değerler genellikle önbellek tarafından daha fazla bellek kullanıldığı anlamına gelir.
 
Maksimum Boyut:
- Tanım: Önbellek için ayrılan maksimum alan miktarı.
 - Önem: Önbelleğin kapasitesini ve veri depolama özelliğini belirler.
 - Uygun bir maksimum boyut belirlemek, önbellek etkinliği ile bellek kullanımı arasında denge kurmaya yardımcı olur. Maksimum boyuta ulaşıldığında, en son kullanılmayan öğe çıkarılarak yeni bir öğe eklenir. Bu durum, verimsizliğe işaret edebilir.
 
En Yüksek Değer:
- Tanım: Oluşturulduğundan beri önbelleğin ulaştığı maksimum boyut.
 - Önem: En yüksek önbellek kullanımı ve olası bellek baskısı hakkında bilgi sağlar.
 - Yüksek su seviyesini izlemek, olası darboğazları veya optimizasyon alanlarını belirlemenize yardımcı olabilir.
 
Taşmalar:
- Tanım: Önbelleğin maksimum boyutunu aşıp yeni girişlere yer açmak için verileri silmek zorunda kaldığı sayı.
 - Önem: Önbellek baskısını ve veri çıkarma nedeniyle olası performans düşüşünü gösterir.
 - Yüksek taşma sayıları, önbellek boyutunun ayarlanması veya önbelleğe alma stratejisinin yeniden değerlendirilmesi gerektiğini gösterir.
 
Aynı istatistikler hata raporunda da bulunabilir.
Önbelleğin boyutunu ayarlama
Önbelleklerin maksimum boyutu vardır. Maksimum önbellek boyutu aşıldığında girişler, en az kullanılan (LRU) sırasına göre çıkarılır.
- Çok az sayıda girişin önbelleğe alınması, önbellek isabet oranını olumsuz etkileyebilir.
 - Çok fazla girişin önbelleğe alınması, önbelleğin bellek kullanımını artırır.
 
Kullanım alanınız için doğru dengeyi bulun.
Gereksiz istemci çağrılarını ortadan kaldırma
İstemciler, kısa bir süre içinde sunucuya aynı sorguyu birden fazla kez gönderebilir:
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();
  }
}
Önceki görüşmelerdeki sonuçları yeniden kullanmayı deneyin:
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();
  }
}
Son sunucu yanıtlarının istemci tarafında ezberlenmesini göz önünde bulundurun
İstemci uygulamaları, API'nin sunucusunun anlamlı yeni yanıtlar üretebileceğinden daha hızlı bir şekilde API'ye sorgu gönderebilir. Bu durumda, son görülen sunucu yanıtını istemci tarafında zaman damgasıyla birlikte not etmek ve not edilen sonuç yeterince yeni ise sunucuya sorgu göndermeden not edilen sonucu döndürmek etkili bir yaklaşımdır. API istemcisi yazarı, notlandırma süresini belirleyebilir.
Örneğin, bir uygulama, çizilen her karedeki istatistikleri sorgulayarak ağ trafiği istatistiklerini kullanıcıya gösterebilir:
@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()));
}
Uygulama, kareleri 60 Hz'de çizebilir. Ancak teorik olarak, TrafficStats içindeki istemci kodu, istatistikler için sunucuya en fazla saniyede bir kez sorgu göndermeyi seçebilir ve önceki sorgudan bir saniye içinde sorgulanırsa en son görülen değeri döndürebilir.
API belgelerinde döndürülen sonuçların güncelliğiyle ilgili herhangi bir sözleşme bulunmadığından bu işleme izin verilir.
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
Sunucu sorguları yerine istemci tarafı kod oluşturmayı kullanma
Sorgu sonuçları derleme sırasında sunucu tarafından biliniyorsa derleme sırasında istemci tarafından da bilinip bilinmediğini ve API'nin tamamen istemci tarafında uygulanıp uygulanamayacağını değerlendirin.
Cihazın kol saati olup olmadığını (yani cihazda Wear OS'in çalışıp çalışmadığını) kontrol eden aşağıdaki uygulama kodunu inceleyin:
public boolean isWatch(Context ctx) {
    PackageManager pm = ctx.getPackageManager();
    return pm.hasSystemFeature(PackageManager.FEATURE_WATCH);
}
Cihazın bu özelliği, derleme sırasında (özellikle Framework'ün bu cihazın önyükleme görüntüsü için derlendiği sırada) bilinir. hasSystemFeature için istemci tarafı kodu, uzak PackageManager sistem hizmetine sorgu göndermek yerine bilinen bir sonucu hemen döndürebilir.
İstemcide sunucu geri aramalarının yinelenmesini önleme
Son olarak, API istemcisi, etkinliklerden haberdar olmak için API sunucusuna geri çağırma işlevleri kaydedebilir.
Uygulamaların aynı temel bilgiler için birden fazla geri çağırma kaydetmesi normaldir. Sunucunun, IPC kullanarak her kayıtlı geri çağırma için istemciye bir kez bildirim göndermesi yerine, istemci kitaplığında sunucuyla IPC kullanarak kayıtlı bir geri çağırma olmalı ve ardından uygulamadaki her kayıtlı geri çağırmaya bildirim göndermelidir.
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"];
  }
}