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 çabuk bitmesinin önemli bir nedenidir. Bu radyolar paket gönderip almak için gereken gücün yanı sıra yalnızca açılıp uyanık kalırken ekstra güç harcar. Her 15 saniyede bir ağ isteği gibi basit bir işlem, mobil radyonun sürekli olarak açık kalmasına ve pil gücünün hızla tükenmesine neden olabilir.

Düzenli olarak yapılan üç genel güncelleme türü vardır:

  • Kullanıcı tarafından başlatılır. Yenilemek için çekme hareketi gibi bazı kullanıcı davranışlarına dayalı bir güncelleme gerçekleştirme.
  • Uygulama tarafından başlatılan. Düzenli olarak güncelleme yapma.
  • Sunucu tarafından başlatıldı. Sunucudan gelen bildirime yanıt olarak güncelleme yapma.

Bu bölümde bunların her biri ele alınmış ve pil tüketimini azaltmak için bunların optimize edilebileceği diğer yöntemler ele alınmaktadı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 ortaya çıkar. Ö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ıtlayın

Kullanıcı tarafından başlatılan bazı istekleri (ör. mevcut veriler hâlâ yeniyken yeni verileri kontrol etmek için kısa bir süre boyunca birden fazla yenilemek için çekme hareketi) göz ardı edebilirsiniz. Her istek üzerine işlem yapmak, radyoyu açık tutarak önemli miktarda güç israfına neden olabilir. Daha etkili bir yaklaşım, kullanıcı tarafından başlatılan istekleri kısmaktır. Böylece belirli bir süre boyunca yalnızca bir istek gönderilebilir. Böylece, radyonun kullanım sıklığı azalır.

Önbellek kullanma

Uygulamanızın verilerini önbelleğe alarak, uygulamanızın referans vermesi gereken bilgilerin yerel bir kopyasını oluşturursunuz. 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 görüntüler gibi isteğe bağlı indirmeler dahil olmak üzere, verileri mümkün olduğunca agresif bir şekilde önbelleğe almalısınız. Önbelleğe alma stratejinizin, uygulamanızda eski veriler göstermesine engel olmak için HTTP önbellek başlıklarını kullanabilirsiniz. Ağ yanıtlarını önbelleğe alma hakkında daha fazla bilgi için Gereksiz indirmeleri ö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ümelerini kullanabilir. Uygulamanızın paylaşılan bir veri kümesine erişmesi gerektiğinde, yeni bir kopya indirmeye çalışmadan önce ilk olarak ö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şme bölümünü 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ğlandığında, daha yüksek bant genişliği genellikle daha yüksek pil maliyetiyle sonuçlanır. Yani 5G, genellikle LTE'den daha fazla enerji tüketir. Bu da 3G'den daha pahalıdır.

Bu, temel radyo durumu radyo teknolojisine göre değişse de, genel anlamda durum değişikliği bekleme süresinin göreli pil etkisi, yüksek bant genişliğine sahip radyolar için daha fazla olduğu anlamına gelir. Kuyruk süresi hakkında daha fazla bilgi için Radyo durumu makinesi bölümüne bakın.

Aynı zamanda, daha yüksek bant genişliği daha agresif bir şekilde önceden getirme ve aynı anda daha fazla veri indirebileceğiniz anlamına gelir. Arka planda pil maliyeti nispeten daha yüksek olduğundan, güncellemelerin sıklığını azaltmak için radyoyu her aktarım oturumu sırasında daha uzun süre etkin tutmak da daha verimli olabilir.

Örneğin, bir LTE radyo, 3G'nin bant genişliği ve enerji maliyetini iki katına çıkarıyorsa her oturum sırasında dört kat daha fazla veya 10 MB'a kadar veri indirmeniz gerekir. Bu kadar fazla veri indirirken, önceden getirmenin mevcut yerel depolama alanı üzerindeki etkisini göz önünde bulundurmak ve önceden getirme önbelleğini düzenli olarak temizlemeniz önemlidir.

Varsayılan ağa bir işleyici kaydetmek için ConnectivityManager simgesini, mevcut cihaz bağlantı türünü belirlemek üzere bir PhoneStateListener işleyici kaydetmek için TelephonyManager simgesini kullanabilirsiniz. Bağlantı türü bilindikten sonra önceden 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 edin

Uygulama tarafından başlatılan istekler genellikle bir plana göre gerçekleşir (ör. arka uç hizmetine günlük veya analizler gönderen uygulamalar). Uygulama tarafından başlatılan istekleri ele alırken bu isteklerin önceliğini, birlikte gruplandırılıp gruplandırılamayacağını ve cihaz şarj olana veya sayaçsız bir ağa bağlanana kadar ertelenip ertelenemeyeceğini 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 tek tek rastgele işlenmesi, önemli miktarda güç tüketebilir ve pil ömrünü azaltabilir. Bir dizi ağ isteğini sıraya almak ve birlikte işlemek daha verimli bir yaklaşımdır. Bu durumda sistem, radyoyu açmanın güç maliyetini yalnızca bir kez öder ve uygulamanın istediği tüm verileri yine de alır.

WorkManager'ı kullanma

Ağ kullanılabilirliği ve güç durumu gibi belirli koşulların karşılanıp karşılanmadığını değerlendiren verimli bir program üzerinde ç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ın olduğunu varsayalım. Bu çalışan, cihazın sınırsız bir ağa bağlı olması ve pil seviyesinin düşük olmaması koşuluyla, aşağıda gösterildiği gibi verilerin alınmasında herhangi bir sorun olması durumunda özel bir yeniden deneme stratejisi ile her saat başı ç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);

Android platformu, WorkManager'a ek olarak yoklama 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 rehberi'ne 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 grubu hakkında bildirim alabilir ve uygulamayı indirir.

Firebase Cloud Messaging ile sunucu güncellemeleri gönderin

Firebase Cloud Messaging (FCM), bir sunucudan belirli bir uygulama örneğine veri aktarmak için kullanılan basit 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 veri sorgulamak için düzenli olarak sunucuya ping göndermesi gereken yoklamaya kıyasla bu etkinliğe dayalı model, uygulamanızın yalnızca indirilecek veri olduğunu bildiğinde yeni bir bağlantı oluşturmasına olanak tanır. Model, gereksiz bağlantıları en aza indirir ve uygulamanızdaki bilgileri güncellerken yaşanan 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 etmesine ve pil ömrü üzerindeki ilgili etkiyi en aza indirmesine olanak tanır.