Permintaan yang dibuat aplikasi Anda ke jaringan adalah penyebab utama terkurasnya baterai karena permintaan tersebut mengaktifkan radio seluler atau Wi-Fi yang mengonsumsi daya. Selain daya yang diperlukan untuk mengirim dan menerima paket, radio ini menggunakan daya ekstra hanya untuk menyala dan tetap aktif. Hal sederhana seperti permintaan jaringan setiap 15 detik dapat membuat radio seluler terus aktif dan menghabiskan daya baterai dengan cepat.
Ada tiga jenis umum update rutin:
- Dimulai oleh pengguna. Melakukan update berdasarkan beberapa perilaku pengguna, seperti gestur tarik untuk memuat ulang.
- Dimulai oleh aplikasi. Melakukan update secara rutin.
- Dimulai oleh server. Melakukan update sebagai respons terhadap notifikasi dari server.
Topik ini membahas setiap komponen tersebut dan membahas cara tambahan untuk mengoptimalkannya guna mengurangi pengurasan baterai.
Mengoptimalkan permintaan yang dimulai pengguna
Permintaan yang dimulai pengguna biasanya terjadi sebagai respons terhadap beberapa perilaku pengguna. Misalnya, aplikasi yang digunakan untuk membaca artikel berita terbaru dapat memungkinkan pengguna melakukan gestur tarik untuk memuat ulang guna memeriksa artikel baru. Anda dapat menggunakan teknik berikut untuk merespons permintaan yang dimulai pengguna sekaligus mengoptimalkan penggunaan jaringan.
Membatasi permintaan pengguna
Anda dapat mengabaikan beberapa permintaan yang dimulai pengguna jika tidak diperlukan, seperti beberapa gestur tarik untuk memuat ulang dalam waktu singkat untuk memeriksa data baru saat data saat ini masih baru. Menindaklanjuti setiap permintaan dapat menghabiskan banyak daya dengan membuat radio tetap aktif. Pendekatan yang lebih efisien adalah dengan men-throttle permintaan yang dimulai pengguna sehingga hanya satu permintaan yang dapat dibuat selama jangka waktu tertentu, sehingga mengurangi frekuensi penggunaan radio.
Menggunakan cache
Dengan meng-cache data aplikasi, Anda membuat salinan lokal informasi yang perlu dirujuk oleh aplikasi Anda. Kemudian, aplikasi Anda dapat mengakses salinan lokal informasi yang sama beberapa kali tanpa harus membuka koneksi jaringan untuk membuat permintaan baru.
Anda harus meng-cache data seagresif mungkin, termasuk resource statis dan download on-demand seperti gambar ukuran penuh. Anda dapat menggunakan header cache HTTP untuk memastikan bahwa strategi caching Anda tidak menyebabkan aplikasi menampilkan data yang sudah tidak berlaku. Untuk informasi selengkapnya tentang menyimpan respons jaringan ke dalam cache, lihat Menghindari download redundan.
Di Android 11 dan yang lebih tinggi, aplikasi Anda dapat menggunakan set data besar yang sama dengan yang digunakan aplikasi lain untuk kasus penggunaan seperti machine learning dan pemutaran media. Saat aplikasi Anda perlu mengakses set data bersama, aplikasi dapat memeriksa versi yang di-cache terlebih dahulu sebelum mencoba mendownload salinan baru. Untuk mempelajari set data bersama lebih lanjut, lihat Mengakses set data bersama.
Mengurangi frekuensi download data lebih banyak dengan menggunakan bandwidth lebih besar
Saat terhubung melalui radio nirkabel, bandwidth yang lebih tinggi umumnya memerlukan konsumsi baterai yang lebih tinggi, yang berarti 5G biasanya mengonsumsi lebih banyak energi daripada LTE, yang pada gilirannya lebih mahal daripada 3G.
Artinya, meskipun status radio yang mendasarinya bervariasi berdasarkan teknologi radio, secara umum dampak relatif baterai dari waktu akhir perubahan status lebih besar untuk radio dengan bandwidth yang lebih tinggi. Untuk mengetahui informasi selengkapnya tentang tail-time, lihat Mesin status radio.
Pada saat yang sama, bandwidth yang lebih tinggi berarti Anda dapat melakukan pengambilan data di awal secara lebih agresif, mendownload lebih banyak data dalam waktu yang sama. Mungkin kurang intuitif, karena biaya baterai waktu tunggu relatif lebih tinggi, juga lebih efisien untuk menjaga radio tetap aktif dalam jangka waktu yang lebih lama selama setiap sesi transfer untuk mengurangi frekuensi update.
Misalnya, jika radio LTE memiliki bandwidth dua kali lipat dan biaya energi dua kali lipat dari 3G, Anda harus mendownload data empat kali lebih banyak selama setiap sesi—atau bahkan hingga 10 MB. Saat mendownload data sebanyak ini, penting untuk mempertimbangkan efek pengambilan data di awal terhadap penyimpanan lokal yang tersedia dan mengosongkan cache pengambilan data di awal secara rutin.
Anda dapat menggunakan
ConnectivityManager untuk mendaftarkan
pemroses untuk jaringan default, dan
TelephonyManager untuk mendaftarkan
PhoneStateListener untuk
menentukan jenis koneksi perangkat saat ini. Setelah jenis koneksi diketahui,
Anda dapat mengubah rutinitas pengambilan data sesuai kebutuhan:
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; }
Mengoptimalkan permintaan yang dimulai aplikasi
Permintaan yang dimulai aplikasi biasanya terjadi sesuai jadwal, seperti aplikasi yang mengirim log atau analisis ke layanan backend. Saat menangani permintaan yang dimulai aplikasi, pertimbangkan prioritas permintaan tersebut, apakah permintaan tersebut dapat dikelompokkan bersama, dan apakah permintaan tersebut dapat ditunda hingga perangkat diisi dayanya atau terhubung ke jaringan tidak berbayar. Permintaan ini dapat dioptimalkan dengan penjadwalan yang cermat dan menggunakan library seperti WorkManager.
Permintaan jaringan batch
Di perangkat seluler, proses mengaktifkan radio, membuat koneksi, dan menjaga radio tetap aktif menggunakan daya yang besar. Oleh karena itu, memproses permintaan individual pada waktu acak dapat menghabiskan daya yang signifikan dan mengurangi masa pakai baterai. Pendekatan yang lebih efisien adalah mengantrekan serangkaian permintaan jaringan dan memprosesnya bersama-sama. Hal ini memungkinkan sistem membayar biaya daya untuk menyalakan radio hanya sekali, dan tetap mendapatkan semua data yang diminta oleh aplikasi.
Menggunakan WorkManager
Anda dapat menggunakan library WorkManager untuk melakukan pekerjaan sesuai jadwal yang efisien
dengan mempertimbangkan apakah kondisi tertentu terpenuhi, seperti ketersediaan jaringan
dan status daya. Misalnya, Anda memiliki subclass Worker bernama DownloadHeadlinesWorker yang mengambil judul berita terbaru. Worker ini
dapat dijadwalkan untuk berjalan setiap jam, asalkan perangkat terhubung ke
jaringan tidak berbayar dan baterai perangkat tidak lemah, dengan strategi percobaan ulang kustom
jika ada masalah saat mengambil data, seperti yang ditunjukkan di bawah ini:
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);
Selain WorkManager, platform Android menyediakan beberapa alat lain untuk membantu Anda membuat jadwal yang efisien untuk menyelesaikan tugas jaringan, seperti polling. Untuk mempelajari lebih lanjut cara menggunakan alat ini, lihat Panduan pemrosesan latar belakang.
Mengoptimalkan permintaan yang dimulai server
Permintaan yang dimulai server biasanya terjadi sebagai respons terhadap notifikasi dari server. Misalnya, aplikasi yang digunakan untuk membaca artikel berita terbaru mungkin menerima notifikasi tentang kumpulan artikel baru yang sesuai dengan preferensi personalisasi pengguna, yang kemudian didownload.
Mengirim update server dengan Firebase Cloud Messaging
Firebase Cloud Messaging (FCM) adalah mekanisme ringan yang digunakan untuk mengirimkan data dari server ke instance aplikasi tertentu. Dengan FCM, server Anda dapat memberi tahu aplikasi yang berjalan di perangkat tertentu bahwa ada data baru yang tersedia untuk aplikasi tersebut.
Dibandingkan dengan polling, yang mengharuskan aplikasi Anda melakukan ping ke server secara rutin untuk mengkueri data baru, model berbasis peristiwa ini memungkinkan aplikasi Anda membuat koneksi baru hanya jika mengetahui ada data yang perlu didownload. Model ini meminimalkan koneksi yang tidak perlu dan mengurangi latensi saat memperbarui informasi dalam aplikasi Anda.
FCM diimplementasikan menggunakan koneksi TCP/IP persisten. Hal ini meminimalkan jumlah koneksi persisten dan memungkinkan platform mengoptimalkan bandwidth serta meminimalkan dampak terkait pada masa pakai baterai.