Düzenli güncellemelerin etkisini en aza indirme

Uygulamanızın ağa gönderdiği istekler, güç tüketen hücresel veya kablosuz radyo dalgalarını açtığı için pil tüketiminin başlıca nedenlerindendir. Bu radyolar, paket gönderip almak için gereken gücün yanı sıra, açıldığında ve uyanıkken de ekstra güç harcar. 15 saniyede bir ağ isteği gibi basit bir işlem, mobil radyoyu sürekli olarak açık tutabilir ve pil gücünü hızla tüketebilir.

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

  • Kullanıcı tarafından başlatılan. Yenilemek için çekme hareketi gibi bazı kullanıcı davranışlarına göre güncelleme yapma.
  • Uygulama tarafından başlatılan. Düzenli bir şekilde güncelleme yapma.
  • Sunucu tarafından başlatılan. Sunucudan gelen bir bildirime yanıt olarak güncelleme yapma.

Bu bölümde, bunların her biri ele alınmış ve pil kullanımını azaltmak için bunların optimize edilebilecek diğer yolları ele alınmıştır.

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

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 makaleleri kontrol etmek için yenilemek için çekme hareketi yapmasına olanak tanıyabilir. 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 kısıtla

Mevcut veriler hâlâ güncel durumdayken yeni verileri kontrol etmek amacıyla kısa bir süreliğine birden fazla yenilemek için çekme hareketi gibi kullanıcı tarafından başlatılan bazı istekleri göz ardı edebilirsiniz. Her bir isteği yerine getirmek, radyoyu uyanık tutarak önemli miktarda güç kaybına neden olabilir. Kullanıcı tarafından başlatılan istekleri kısıtlayarak belirli bir süre içinde yalnızca bir istekte bulunulabilir ve böylece radyo kullanım sıklığının azaltılması daha etkili bir yaklaşım olur.

Önbellek kullanma

Uygulamanızın verilerini önbelleğe alarak, uygulamanızın referans vermesi gereken bilgilerin yerel bir kopyasını oluşturmuş olursunuz. Böylece uygulamanız, yeni isteklerde bulunmak için bir 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 dahil olmak üzere verileri mümkün olduğunca yüksek düzeyde önbelleğe almalısınız. Önbelleğe alma stratejinizin uygulamanızın eski veriler göstermesine neden olmaması için HTTP önbellek üst bilgilerini kullanabilirsiniz. Ağ yanıtlarını önbelleğe alma hakkında daha fazla bilgi için Gereksiz indirme işlemlerini önleme bölümüne bakın.

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ümelerinin aynısını kullanabilir. Uygulamanızın paylaşılan bir veri kümesine erişmesi gerektiğinde, yeni bir kopya indirmeyi denemeden önce ilk olarak önbelleğe alınan sürüm olup olmadığını kontrol edebilir. Paylaşılan veri kümeleri hakkında daha fazla bilgi edinmek için Paylaşılan veri kümelerine erişme bölümüne bakın.

Daha seyrek olarak daha fazla veri indirmek için daha fazla bant genişliği kullanma

Kablosuz bir radyo üzerinden bağlandığınızda, daha yüksek bant genişliği genellikle daha yüksek pil maliyetinden kaynaklanır. Diğer bir deyişle 5G, genellikle LTE'den daha fazla enerji tüketir. Bu da 3G'den daha pahalıdır.

Bu da temel radyo durumunun radyo teknolojisine bağlı olarak değişeceği anlamına gelir. Genellikle ifade edildiğinde, durum değişikliğinin kuyruk zamanının göreceli pil etkisi, yüksek bant genişliğine sahip radyolarda daha fazladır. Kuyruk zamanı hakkında daha fazla bilgi için Radyo durumu makinesi konusuna bakın.

Aynı zamanda, daha yüksek bant genişliği, daha agresif bir şekilde önceden getirme yapabileceğiniz ve aynı zamanda daha fazla veri indirebileceğiniz anlamına gelir. Belki de daha pratik olmasa da, kuyruk zamanı pil maliyeti nispeten daha yüksek olduğundan, güncelleme sıklığını azaltmak için her aktarım oturumunda radyoyu daha uzun süre etkin tutmak da daha verimlidir.

Örneğin, bir LTE radyo, bant genişliğinin ve 3G'nin enerji maliyetini iki katına çıkarsa her oturumda dört kat veya muhtemelen 10 MB'a kadar veri indirmeniz gerekir. Bu kadar fazla veri indirirken önceden getirme işleminizin mevcut yerel depolama alanı üzerindeki etkisini göz önünde bulundurmanız ve önceden getirme önbelleğinizi düzenli olarak temizlemeniz gerekir.

Varsayılan ağın bir işleyicisi kaydetmek için ConnectivityManager, geçerli cihaz bağlantı türünü belirlemek amacıyla bir PhoneStateListener kaydettirmek için TelephonyManager kullanabilirsiniz. Bağlantı türünü öğrendikten sonra, önceden getirme rutinlerinizi uygun şekilde 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 bir program çerçevesinde gerçekleşir. Uygulama tarafından başlatılan isteklerle ilgilenirken bu isteklerin önceliğini, isteklerin bir araya getirilip toplu hale getirilemeyeceğini ve cihaz şarj edilene veya sınırsız bir ağa bağlanana kadar ertelenip ertelenemeyeceğini göz önünde bulundurun. Bu istekler, dikkatli bir planlama yapılarak ve WorkManager gibi kitaplıklar kullanılarak optimize edilebilir.

Toplu ağ istekleri

Mobil cihazlarda radyoyu açma, bağlantı kurma ve radyoyu uyanık tutma işlemleri büyük miktarda güç kullanır. Bu nedenle, isteklerin rastgele zamanlarda işlenmesi önemli miktarda güç tüketebilir ve pil ömrünü azaltabilir. Bir grup ağ isteğini sıraya almak ve bunları birlikte işlemek daha verimli bir yaklaşımdır. Bu sayede sistem, radyoyu açmanın güç maliyetini bir kez öder ve bir 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ın değerlendirildiği verimli bir program doğrultusunda çalışmak için WorkManager kitaplığını kullanabilirsiniz. Örneğin, en son haber başlıklarını alan DownloadHeadlinesWorker adında bir Worker alt sınıfınız olduğunu varsayalım. Bu çalışan, aşağıda gösterildiği gibi, veri almayla ilgili herhangi bir sorun yaşanıyorsa özel bir yeniden deneme stratejisiyle, cihazın sınırsız bir ağa bağlı olması ve cihazın pilinin düşük olmaması koşuluyla her saat çalışacak şekilde planlanabilir:

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);

WorkManager'ın yanı sıra Android platformu, anket gibi ağ iletişimi görevlerini tamamlamak için verimli bir program oluşturmanıza yardımcı olacak çeşitli araçlar sunar. Bu araçların kullanımı hakkında daha fazla bilgi edinmek için Arka planda işleme rehberine bakın.

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

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 uyan yeni bir makale grubuyla ilgili bildirim alabilir ve daha sonra bu makaleleri indirir.

Firebase Cloud Messaging ile sunucu güncellemeleri gönderin

Firebase Cloud Messaging (FCM), bir sunucudan belirli bir uygulama örneğine veri iletmek için kullanılan hafif bir mekanizmadır. Sunucunuz FCM'yi kullanarak, belirli bir cihazda çalışan uygulamanıza kendisi için yeni veriler olduğunu bildirebilir.

Uygulamanızın yeni verileri sorgulamak için düzenli olarak sunucuyu pinglemesinin gerektiği yoklamaya kıyasla bu olay odaklı model, uygulamanızın yalnızca indirilecek veri olduğunu bildiği durumlarda yeni bağlantı oluşturmasına olanak tanır. Bu 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, kalıcı bağlantı sayısını en aza indirir ve platformun bant genişliğini optimize etmesini ve pil ömrü üzerindeki ilgili etkiyi en aza indirmesini sağlar.