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.