Düzenli güncellemelerin etkisini en aza indirme

Uygulamanızın ağa gönderdiği istekler, güç tüketen hücresel veya kablosuz radyoları açtığı için pilin hızlı tükenmesine neden olur. Bu radyolar, paket gönderip almak için gereken gücün yanı sıra yalnızca açılmak ve açık kalmak için de ek güç harcar. 15 saniyede bir ağ isteği gibi basit bir işlem, mobil radyoyu sürekli açık tutabilir ve pil gücünü hızlıca tüketebilir.

Üç genel türde düzenli güncelleme vardır:

  • Kullanıcı tarafından başlatılır. Yenileme için ekranı çekme gibi bazı kullanıcı davranışlarına dayalı güncelleme yapma
  • Uygulama tarafından başlatıldı. Yinelenen bir güncelleme gerçekleştirme.
  • Sunucu tarafından başlatılır. Bir sunucudan gelen bildirime yanıt olarak güncelleme yapma

Bu konuda, bunların her biri ele alınmakta ve pil tüketimini azaltmak için optimize edilebilecek ek yöntemler tartışılmaktadır.

Kullanıcı tarafından başlatılan istekleri optimize etme

Kullanıcı tarafından başlatılan istekler genellikle bazı kullanıcı davranışlarına yanıt olarak gerçekleşir. Örneğin, en son haber makalelerini okumak için kullanılan bir uygulama, kullanıcının yeni makale olup olmadığını kontrol etmek için "yenilemek için sürükle" hareketini gerçekleştirmesine izin verebilir. Ağ kullanımını optimize ederken kullanıcı tarafından başlatılan isteklere yanıt vermek için aşağıdaki teknikleri kullanabilirsiniz.

Kullanıcı isteklerini azaltma

Gerekmediği durumlarda kullanıcı tarafından başlatılan bazı istekleri yoksayabilirsiniz. Örneğin, mevcut veriler henüz güncelken yeni veriler olup olmadığını kontrol etmek için kısa bir süre içinde birden fazla yenileme hareketi yapılması gibi. Her isteği yerine getirmek, radyoyu açık tutarak önemli miktarda güç israfına neden olabilir. Daha verimli bir yaklaşım, kullanıcı tarafından başlatılan istekleri sınırlandırarak bir süre boyunca yalnızca bir istek gönderilmesini sağlamaktır. Bu sayede radyonun kullanım sıklığı azalır.

Önbellek kullanma

Uygulamanızın verilerini önbelleğe alarak uygulamanızın referans alması gereken bilgilerin yerel bir kopyasını oluşturursunuz. Ardından uygulamanız, yeni istek göndermek için ağ bağlantısı açmak zorunda kalmadan bilgilerin aynı yerel kopyasına birden çok kez erişebilir.

Statik kaynaklar ve tam boyutlu resimler gibi isteğe bağlı indirmeler de dahil olmak üzere verileri mümkün olduğunca agresif bir şekilde önbelleğe almanız gerekir. Önbelleğe alma stratejinizin uygulamanızın eski veriler göstermesine neden olmaması için HTTP önbelleğe alma üstbilgilerini kullanabilirsiniz. Ağ yanıtlarını önbelleğe alma hakkında daha fazla bilgi için Gereksiz indirmelerden kaçınma başlıklı makaleyi inceleyin.

Android 11 ve sonraki sürümlerde uygulamanız, makine öğrenimi ve medya oynatma gibi kullanım alanları için diğer uygulamaların kullandığı büyük veri kümelerini kullanabilir. Uygulamanızın paylaşılan bir veri kümesine erişmesi gerektiğinde, yeni bir kopya indirmeye çalışmadan önce önce önbelleğe alınmış bir sürümü kontrol edebilir. Paylaşılan veri kümeleri hakkında daha fazla bilgi edinmek için Paylaşılan veri kümelerine erişim başlıklı makaleyi inceleyin.

Daha az sıklıkta daha fazla veri indirmek için daha yüksek bant genişliği kullanın

Kablosuz radyo üzerinden bağlanırken daha yüksek bant genişliği genellikle daha yüksek pil maliyeti anlamına gelir. Yani 5G, genellikle LTE'den daha fazla enerji tüketir ve LTE de 3G'den daha pahalıdır.

Bu, temel radyo durumu radyo teknolojisine göre değişse de genel olarak durum değişikliği son zamanının pil üzerindeki göreceli etkisinin daha yüksek bant genişliğine sahip radyolar için daha büyük olduğu anlamına gelir. Son süre hakkında daha fazla bilgi için Radyo durum makinesi bölümüne bakın.

Aynı zamanda, daha yüksek bant genişliği, daha agresif ön getirme yapabileceğiniz ve aynı süre içinde daha fazla veri indirebileceğiniz anlamına gelir. Son zamanlı pil maliyeti nispeten daha yüksek olduğundan, güncellemelerin sıklığını azaltmak için her aktarım oturumunda radyoyu daha uzun süre etkin tutmak da daha verimlidir.

Örneğin, bir LTE radyosunun bant genişliği ve enerji maliyeti 3G'nin iki katıysa her oturumda dört kat daha fazla veri indirmeniz gerekir. Bu miktar, 10 MB'a kadar çıkabilir. Bu kadar veri indirirken ön getirme işleminin mevcut yerel depolama alanı üzerindeki etkisini göz önünde bulundurmanız ve ön getirme önbelleğinizi düzenli olarak temizlemeniz önemlidir.

Varsayılan ağ için bir dinleyici kaydetmek üzere ConnectivityManager, mevcut cihaz bağlantı türünü belirlemek üzere PhoneStateListener kaydetmek için TelephonyManager kullanabilirsiniz. Bağlantı türü bilindikten sonra ön getirme rutinlerinizi buna göre değiştirebilirsiniz:

Kotlin

val cm = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val tm = getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager

private var hasWifi = false
private var hasCellular = false
private var cellModifier: Float = 1f

private val networkCallback = object : ConnectivityManager.NetworkCallback() {
    // Network capabilities have changed for the network
    override fun onCapabilitiesChanged(
            network: Network,
            networkCapabilities: NetworkCapabilities
    ) {
        super.onCapabilitiesChanged(network, networkCapabilities)
        hasCellular = networkCapabilities
    .hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)
        hasWifi = networkCapabilities
    .hasTransport(NetworkCapabilities.TRANSPORT_WIFI)
    }
}

private val phoneStateListener = object : PhoneStateListener() {
override fun onPreciseDataConnectionStateChanged(
    dataConnectionState: PreciseDataConnectionState
) {
  cellModifier = when (dataConnectionState.networkType) {
      TelephonyManager.NETWORK_TYPE_LTE or TelephonyManager.NETWORK_TYPE_HSPAP -> 4f
      TelephonyManager.NETWORK_TYPE_EDGE or TelephonyManager.NETWORK_TYPE_GPRS -> 1/2f
      else -> 1f

  }
}

private class NetworkState {
    private var defaultNetwork: Network? = null
    private var defaultCapabilities: NetworkCapabilities? = null
    fun setDefaultNetwork(network: Network?, caps: NetworkCapabilities?) = synchronized(this) {
        defaultNetwork = network
        defaultCapabilities = caps
    }
    val isDefaultNetworkWifi
        get() = synchronized(this) {
            defaultCapabilities?.hasTransport(TRANSPORT_WIFI) ?: false
        }
    val isDefaultNetworkCellular
        get() = synchronized(this) {
            defaultCapabilities?.hasTransport(TRANSPORT_CELLULAR) ?: false
        }
    val isDefaultNetworkUnmetered
        get() = synchronized(this) {
            defaultCapabilities?.hasCapability(NET_CAPABILITY_NOT_METERED) ?: false
        }
    var cellNetworkType: Int = TelephonyManager.NETWORK_TYPE_UNKNOWN
        get() = synchronized(this) { field }
        set(t) = synchronized(this) { field = t }
    private val cellModifier: Float
        get() = synchronized(this) {
            when (cellNetworkType) {
                TelephonyManager.NETWORK_TYPE_LTE or TelephonyManager.NETWORK_TYPE_HSPAP -> 4f
                TelephonyManager.NETWORK_TYPE_EDGE or TelephonyManager.NETWORK_TYPE_GPRS -> 1 / 2f
                else -> 1f
            }
        }
    val prefetchCacheSize: Int
        get() = when {
            isDefaultNetworkWifi -> MAX_PREFETCH_CACHE
            isDefaultNetworkCellular -> (DEFAULT_PREFETCH_CACHE * cellModifier).toInt()
            else -> DEFAULT_PREFETCH_CACHE
        }
}
private val networkState = NetworkState()
private val networkCallback = object : ConnectivityManager.NetworkCallback() {
    // Network capabilities have changed for the network
    override fun onCapabilitiesChanged(
            network: Network,
            networkCapabilities: NetworkCapabilities
    ) {
        networkState.setDefaultNetwork(network, networkCapabilities)
    }

    override fun onLost(network: Network?) {
        networkState.setDefaultNetwork(null, null)
    }
}

private val telephonyCallback = object : TelephonyCallback(), TelephonyCallback.PreciseDataConnectionStateListener {
    override fun onPreciseDataConnectionStateChanged(dataConnectionState: PreciseDataConnectionState) {
        networkState.cellNetworkType = dataConnectionState.networkType
    }
}

connectivityManager.registerDefaultNetworkCallback(networkCallback)
telephonyManager.registerTelephonyCallback(telephonyCallback)


private val prefetchCacheSize: Int
get() {
    return when {
        hasWifi -> MAX_PREFETCH_CACHE
        hasCellular -> (DEFAULT_PREFETCH_CACHE * cellModifier).toInt()
        else -> DEFAULT_PREFETCH_CACHE
    }
}

}

Java

ConnectivityManager cm =
 (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
TelephonyManager tm =
  (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);

private boolean hasWifi = false;
private boolean hasCellular = false;
private float cellModifier = 1f;

private ConnectivityManager.NetworkCallback networkCallback = new ConnectivityManager.NetworkCallback() {
@Override
public void onCapabilitiesChanged(
    @NonNull Network network,
    @NonNull NetworkCapabilities networkCapabilities
) {
        super.onCapabilitiesChanged(network, networkCapabilities);
        hasCellular = networkCapabilities
    .hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR);
        hasWifi = networkCapabilities
    .hasTransport(NetworkCapabilities.TRANSPORT_WIFI);
}
};

private PhoneStateListener phoneStateListener = new PhoneStateListener() {
@Override
public void onPreciseDataConnectionStateChanged(
    @NonNull PreciseDataConnectionState dataConnectionState
    ) {
    switch (dataConnectionState.getNetworkType()) {
        case (TelephonyManager.NETWORK_TYPE_LTE |
            TelephonyManager.NETWORK_TYPE_HSPAP):
            cellModifier = 4;
            Break;
        case (TelephonyManager.NETWORK_TYPE_EDGE |
            TelephonyManager.NETWORK_TYPE_GPRS):
            cellModifier = 1/2.0f;
            Break;
        default:
            cellModifier = 1;
            Break;
    }
}
};

cm.registerDefaultNetworkCallback(networkCallback);
tm.listen(
phoneStateListener,
PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE
);

public int getPrefetchCacheSize() {
if (hasWifi) {
    return MAX_PREFETCH_SIZE;
}
if (hasCellular) {
    return (int) (DEFAULT_PREFETCH_SIZE * cellModifier);
    }
return DEFAULT_PREFETCH_SIZE;
}

Uygulama tarafından başlatılan istekleri optimize etme

Uygulama tarafından başlatılan istekler genellikle bir arka uç hizmetine günlük veya analiz gönderen bir uygulama gibi planlı olarak gerçekleşir. Uygulama tarafından başlatılan isteklerle çalışırken bu isteklerin önceliğini, birlikte gruplandırılıp gruplandırılamayacağını ve cihaz şarj olana veya sınırsız bir ağa bağlanana kadar ertelenebilir olup olmadığını göz önünde bulundurun. Bu istekler, dikkatli bir planlamayla ve WorkManager gibi kitaplıklar kullanılarak optimize edilebilir.

Toplu ağ istekleri

Mobil cihazlarda radyoyu açma, bağlantı kurma ve radyoyu açık tutma işlemleri çok fazla güç tüketir. Bu nedenle, isteklerin rastgele zamanlarda işlenmesi önemli miktarda güç tüketimine neden olabilir ve pil ömrünü kısaltabilir. Daha verimli bir yaklaşım, bir dizi ağ isteğini sıraya ekleyip birlikte işlemek olacaktır. Bu sayede sistem, radyoyu açmanın güç maliyetini yalnızca bir kez öder ve uygulamanın istediği tüm verileri almaya devam eder.

WorkManager'ı kullanma

Ağ kullanılabilirliği ve güç durumu gibi belirli koşulların karşılanıp karşılanmadığını dikkate alan verimli bir programda çalışma yapmak için WorkManager kitaplığını kullanabilirsiniz. Örneğin, en son haber başlıklarını alan DownloadHeadlinesWorker adlı bir Worker alt sınıfınız olduğunu varsayalım. Bu işleyici, cihazın sınırsız bir ağa bağlı olması ve pil seviyesinin düşük olmaması koşuluyla her saat çalıştırılacak şekilde planlanabilir. Verileri almayla ilgili herhangi bir sorun olursa özel bir yeniden deneme stratejisi uygulanır. Bu strateji aşağıda gösterilmiştir:

Kotlin

val constraints = Constraints.Builder()
    .setRequiredNetworkType(NetworkType.UNMETERED)
    .setRequiresBatteryNotLow(true)
    .build()
val request =
    PeriodicWorkRequestBuilder<DownloadHeadlinesWorker>(1, TimeUnit.HOURS)
        .setConstraints(constraints)
        .setBackoffCriteria(BackoffPolicy.LINEAR, 1L, TimeUnit.MINUTES)
        .build()
WorkManager.getInstance(context).enqueue(request)

Java

Constraints constraints = new Constraints.Builder()
        .setRequiredNetworkType(NetworkType.UNMETERED)
        .setRequiresBatteryNotLow(true)
        .build();
WorkRequest request = new PeriodicWorkRequest.Builder(DownloadHeadlinesWorker.class, 1, TimeUnit.HOURS)
        .setBackoffCriteria(BackoffPolicy.LINEAR, 1L, TimeUnit.MINUTES)
        .build();
WorkManager.getInstance(this).enqueue(request);

Android platformu, WorkManager'a ek olarak, anket gibi ağ görevlerini tamamlamak için verimli bir program oluşturmanıza yardımcı olacak başka araçlar da sunar. Bu araçları kullanma hakkında daha fazla bilgi edinmek için Arka planda işleme kılavuzuna bakın.

Sunucu tarafından başlatılan istekleri optimize etme

Sunucu tarafından başlatılan istekler genellikle bir sunucudan gelen bildirime yanıt olarak gerçekleşir. Örneğin, en son haber makalelerini okumak için kullanılan bir uygulama, kullanıcının kişiselleştirme tercihlerine uygun yeni bir makale grubuyla ilgili bildirim alabilir ve bu makaleleri indirebilir.

Firebase Cloud Messaging ile sunucu güncellemeleri gönderme

Firebase Cloud Messaging (FCM), bir sunucudan belirli bir uygulama örneğine veri aktarmak için kullanılan hafif bir mekanizmadır. Sunucunuz, FCM'yi kullanarak belirli bir cihazda çalışan uygulamanızı yeni verilerin kullanılabilir olduğu konusunda bilgilendirebilir.

Uygulamanızın yeni verileri sorgulamak için sunucuyu düzenli olarak pinglemesi gereken ankete kıyasla bu olaya dayalı model, uygulamanızın yalnızca indirilecek veri olduğunu bildiği durumlarda yeni bir bağlantı oluşturmasına olanak tanır. Model, gereksiz bağlantıları en aza indirir ve uygulamanızdaki bilgileri güncellerken gecikmeyi azaltır.

FCM, kalıcı bir TCP/IP bağlantısı kullanılarak uygulanır. Bu sayede kalıcı bağlantıların sayısı en aza indirilir ve platformun bant genişliğini optimize etmesine ve pil ömrü üzerindeki etkisini en aza indirmesine olanak tanınmış olur.