Meminimalkan efek update reguler

Permintaan yang dibuat aplikasi Anda ke jaringan adalah penyebab utama baterai cepat habis karena menyalakan radio seluler atau Wi-Fi yang mengonsumsi daya. Di luar kemampuan yang diperlukan untuk mengirim dan menerima paket, radio ini menghabiskan daya ekstra menyala dan tetap terjaga. Sesuatu yang sederhana seperti permintaan jaringan setiap 15 detik dapat membuat radio seluler aktif secara terus menerus dan menghabiskan daya baterai dengan cepat.

Ada tiga jenis umum update reguler:

  • Dimulai oleh pengguna. Menjalankan update berdasarkan beberapa perilaku pengguna, seperti gestur tarik untuk refresh.
  • Dimulai aplikasi. Melakukan update secara berulang.
  • Dimulai oleh server. Melakukan pembaruan sebagai tanggapan atas notifikasi dari server.

Topik ini membahas masing-masing hal tersebut dan membahas cara-cara lain yang dapat dilakukan dioptimalkan untuk mengurangi konsumsi baterai.

Mengoptimalkan permintaan yang dimulai pengguna

Permintaan yang dimulai pengguna biasanya terjadi sebagai respons terhadap beberapa perilaku pengguna. Sebagai misalnya, aplikasi yang digunakan untuk membaca artikel berita terbaru memungkinkan pengguna lakukan gestur pull-to-refresh untuk memeriksa artikel baru. Anda dapat menggunakan teknik berikut untuk merespons permintaan yang dimulai pengguna sambil mengoptimalkan penggunaan jaringan.

Membatasi permintaan pengguna

Anda dapat mengabaikan beberapa permintaan yang dimulai oleh pengguna jika tidak ada kebutuhan untuk mereka, seperti beberapa gerakan tarik untuk menyegarkan dalam waktu singkat untuk memeriksa data baru selagi data saat ini masih baru. Bertindak untuk setiap dapat menyia-nyiakan banyak daya dengan menyalakan radio. J pendekatan yang lebih efisien adalah membatasi permintaan yang dimulai oleh pengguna sehingga hanya satu permintaan yang dapat dibuat selama periode waktu tertentu, mengurangi seberapa sering radio digunakan.

Menggunakan cache

Dengan menyimpan data aplikasi dalam cache, Anda membuat salinan lokal informasi yang perlu dirujuk oleh aplikasi. Aplikasi Anda kemudian dapat mengakses salinan informasi berkali-kali tanpa harus membuka jaringan membuat permintaan baru.

Anda harus meng-cache data seaktif mungkin, termasuk data statis sumber daya dan download on-demand seperti gambar ukuran penuh. Anda dapat menggunakan HTTP header cache untuk memastikan strategi penyimpanan cache Anda tidak mengakibatkan aplikasi menampilkan data usang. Untuk informasi selengkapnya tentang tanggapan jaringan dalam cache, lihat Hindari redundansi download.

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. Jika aplikasi perlu mengakses set data bersama, aplikasi dapat memeriksa versi yang di-cache terlebih dahulu sebelum mencoba mengunduh salinan yang baru. Untuk mempelajari lebih lanjut tentang {i>dataset<i} bersama, lihat Mengakses set data bersama.

Mengurangi frekuensi download data lebih banyak dengan menggunakan bandwidth lebih besar

Ketika terhubung melalui radio nirkabel, {i>bandwidth<i} yang lebih tinggi umumnya harga biaya baterai yang lebih tinggi, yang berarti bahwa 5G biasanya menggunakan lebih banyak energi daripada LTE, yang pada gilirannya lebih mahal daripada 3G.

Artinya, meskipun status radio yang mendasarinya bervariasi berdasarkan teknologi radio, secara umum berbicara tentang dampak baterai relatif terhadap keadaan {i>tail-time<i} perubahan lebih besar untuk radio dengan bandwidth lebih tinggi. Untuk mengetahui informasi selengkapnya tentang tail-time, lihat The radio state komputer.

Pada saat yang sama, bandwidth yang lebih tinggi berarti Anda dapat mengambil lebih banyak data secara agresif, mengunduh lebih banyak data dalam waktu yang sama. Mungkin hal tersebut kurang intuitif, karena penggunaan daya baterai pada waktu akhir akan relatif lebih tinggi, tetapi juga lebih efisien untuk membuat radio tetap aktif selama jangka waktu yang lebih lama bagi setiap sesi transfer guna mengurangi frekuensi update.

Misalnya, jika radio LTE memiliki bandwidth dua kali lipat dan biaya energi dua kali lipat atau jaringan 3G, Anda harus mengunduh data empat kali lebih banyak setiap sesi—atau hingga 10 MB. Saat mendownload data sebanyak ini, sebaiknya pertimbangkan pengaruh dari melakukan prefetch di penyimpanan lokal yang tersedia dan hapus cache prefetch Anda secara teratur.

Anda dapat menggunakan ConnectivityManager untuk mendaftar pemroses untuk jaringan default, dan TelephonyManager untuk mendaftar PhoneStateListener ke menentukan jenis koneksi perangkat saat ini. Setelah jenis koneksi diketahui, Anda dapat mengubah rutinitas pengambilan data dengan sesuai:

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 oleh aplikasi

Permintaan yang dimulai oleh aplikasi biasanya terjadi sesuai jadwal, seperti aplikasi yang mengirimkan log atau analisis pada layanan backend. Saat menangani permintaan yang dimulai aplikasi, pertimbangkan prioritas permintaan tersebut, apakah permintaan tersebut dapat digabungkan bersama, dan apakah permintaan tersebut dapat ditangguhkan hingga perangkat mengisi daya atau terhubung ke jaringan tanpa kuota. Permintaan ini dapat dioptimalkan dengan penjadwalan yang cermat dan dengan menggunakan library seperti WorkManager.

Permintaan jaringan batch

Pada perangkat seluler, proses menyalakan radio, membuat koneksi, dan menjaga agar radio tetap aktif memakai daya dalam jumlah besar. Karena alasan ini, memproses permintaan individu pada waktu acak dapat menghabiskan daya yang signifikan dan mengurangi masa pakai baterai. Pendekatan yang lebih efisien adalah mengantrekan satu set jaringan permintaan dan memprosesnya bersama-sama. Hal ini memungkinkan sistem untuk membayar daya menyalakan radio satu kali saja, dan tetap mendapatkan semua data yang diminta aplikasi.

Menggunakan WorkManager

Anda dapat menggunakan library WorkManager untuk melakukan pekerjaan sesuai jadwal yang efisien yang mempertimbangkan apakah kondisi tertentu terpenuhi, seperti ketersediaan jaringan dan status daya. Misalnya, Anda memiliki Subclass Worker dipanggil DownloadHeadlinesWorker yang mengambil judul berita terbaru. Pekerja ini dapat dijadwalkan untuk berjalan setiap jam, asalkan perangkat terhubung ke jaringan tanpa kuota dan baterai perangkat tidak rendah, dengan strategi percobaan ulang kustom jika ada masalah saat mengambil data, seperti yang ditunjukkan di bawah:

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 lainnya untuk membantu Anda membuat jadwal yang efisien guna menyelesaikan tugas jaringan, seperti sebagai polling. Untuk mempelajari lebih lanjut cara menggunakan alat ini, lihat Panduan untuk pemrosesan latar belakang.

Mengoptimalkan permintaan yang dimulai server

Permintaan yang dimulai server biasanya terjadi sebagai respons atas notifikasi dari server tertentu. Misalnya, aplikasi yang digunakan untuk membaca artikel berita terbaru mungkin menerima notifikasi tentang sekumpulan artikel baru yang sesuai dengan preferensi personalisasi, yang kemudian diunduh.

Mengirim update server dengan Firebase Cloud Messaging

Firebase Cloud Messaging (FCM) bersifat ringan mekanisme yang digunakan untuk mengirimkan data dari server ke {i>instance<i} aplikasi tertentu. Dengan FCM, server dapat memberi tahu aplikasi yang berjalan di perangkat tertentu bahwa ada data baru yang tersedia.

Dibandingkan dengan polling, di mana aplikasi Anda harus secara teratur melakukan {i>ping<i} ke server untuk melakukan kueri data baru, model berbasis peristiwa ini memungkinkan aplikasi Anda membuat koneksi baru hanya ketika ia mengetahui ada data untuk diunduh. Model ini meminimalkan kebutuhan dan mengurangi latensi saat memperbarui informasi dalam aplikasi.

FCM diimplementasikan menggunakan koneksi TCP/IP persisten. Hal ini meminimalkan jumlah koneksi persisten dan memungkinkan platform untuk mengoptimalkan bandwidth dan meminimalkan dampak terkait masa pakai baterai.