Pengoptimalan latar belakang

Proses latar belakang dapat menggunakan banyak memori dan daya baterai. Misalnya, siaran implisit dapat memulai banyak proses latar belakang yang telah mendaftar untuk memantau siaran tersebut, meskipun proses itu mungkin tidak begitu berguna. Hal ini dapat berdampak besar pada performa perangkat dan pengalaman pengguna.

Untuk mengatasi masalah ini, Android 7.0 (API level 24) menerapkan pembatasan berikut:

Jika aplikasi Anda menggunakan salah satu intent ini, Anda harus menghapus dependensi pada intent tersebut sesegera mungkin agar dapat menargetkan perangkat yang menjalankan Android 7.0 atau yang lebih tinggi dengan tepat. Framework Android menyediakan beberapa solusi untuk mengurangi kebutuhan akan siaran implisit ini. Misalnya, JobScheduler dan WorkManager yang baru menyediakan mekanisme andal untuk menjadwalkan operasi jaringan jika kondisi tertentu terpenuhi, misalnya koneksi ke jaringan tidak berbayar. Sekarang Anda juga dapat menggunakan JobScheduler untuk merespons perubahan pada penyedia konten. Objek JobInfo membuat enkapsulasi parameter yang digunakan JobScheduler untuk menjadwalkan tugas Anda. Saat kondisi tugas terpenuhi, sistem akan mengeksekusi tugas ini di JobService aplikasi Anda.

Di halaman ini, kita akan mempelajari cara menggunakan metode alternatif, seperti JobScheduler, untuk menyesuaikan aplikasi Anda dengan pembatasan baru ini.

Larangan yang diinisialisasi pengguna

Di halaman Penggunaan baterai dalam setelan sistem, pengguna dapat memilih dari opsi berikut:

  • Tidak dibatasi: Mengizinkan semua pekerjaan di latar belakang, yang mungkin menggunakan lebih banyak daya baterai.
  • Dioptimalkan (default): Mengoptimalkan kemampuan aplikasi untuk melakukan pekerjaan di latar belakang, berdasarkan cara pengguna berinteraksi dengan aplikasi.
  • Dibatasi: Mencegah aplikasi berjalan di latar belakang sepenuhnya. Apl mungkin tidak berfungsi dengan benar.

Jika aplikasi menunjukkan beberapa perilaku buruk yang dijelaskan dalam Android vitals, sistem mungkin meminta pengguna untuk membatasi akses aplikasi tersebut ke resource sistem.

Jika sistem mendeteksi bahwa aplikasi menggunakan terlalu banyak resource, pengguna akan diberi tahu dan diberi opsi untuk membatasi tindakan aplikasi. Perilaku yang dapat memicu pemberitahuan ini meliputi:

  • Penguncian layar saat aktif yang berlebihan: 1 penguncian layar saat aktif parsial ditahan selama satu jam saat layar nonaktif
  • Layanan latar belakang yang berlebihan: Jika aplikasi menargetkan level API di bawah 26 dan memiliki layanan latar belakang yang berlebihan

Pembatasan pasti yang diberlakukan ditentukan oleh produsen perangkat. Misalnya, pada build AOSP yang menjalankan Android 9 (API level 28) atau yang lebih tinggi, aplikasi yang berjalan di latar belakang dalam status "dibatasi" memiliki batasan berikut:

  • Tidak dapat meluncurkan layanan latar depan
  • Layanan latar depan yang ada dihapus dari latar depan
  • Alarm tidak terpicu
  • Tugas tidak dijalankan

Selain itu, jika aplikasi menargetkan Android 13 (API level 33) atau yang lebih tinggi dan berada dalam status "dibatasi", sistem tidak akan mengirimkan siaran BOOT_COMPLETED atau siaran LOCKED_BOOT_COMPLETED hingga aplikasi dimulai untuk alasan lain.

Pembatasan spesifik tercantum dalam Pembatasan pengelolaan daya.

Pembatasan untuk menerima siaran aktivitas jaringan

Aplikasi yang menargetkan Android 7.0 (API level 24) tidak menerima siaran CONNECTIVITY_ACTION jika sudah mendaftar untuk menerima siaran tersebut dalam manifesnya. Semua proses yang bergantung pada siaran ini tidak akan dimulai. Hal ini dapat menimbulkan masalah pada aplikasi yang ingin memproses perubahan jaringan atau menjalankan aktivitas jaringan massal saat perangkat tersambung ke jaringan tidak berbayar. Beberapa solusi untuk mengatasi pembatasan ini sudah disediakan dalam framework Android, tetapi pemilihan solusi yang tepat bergantung pada apa yang Anda ingin aplikasi capai.

Catatan: BroadcastReceiver yang terdaftar dengan Context.registerReceiver() akan terus menerima siaran ini saat aplikasi berjalan.

Menjadwalkan tugas jaringan pada koneksi tidak berbayar

Saat menggunakan class JobInfo.Builder untuk membuat objek JobInfo, terapkan metode setRequiredNetworkType() dan teruskan JobInfo.NETWORK_TYPE_UNMETERED sebagai parameter tugas. Contoh kode berikut menjadwalkan layanan untuk berjalan saat perangkat tersambung ke jaringan tidak berbayar dan mengisi daya:

Kotlin

const val MY_BACKGROUND_JOB = 0
...
fun scheduleJob(context: Context) {
    val jobScheduler = context.getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler
    val job = JobInfo.Builder(
            MY_BACKGROUND_JOB,
            ComponentName(context, MyJobService::class.java)
    )
            .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)
            .setRequiresCharging(true)
            .build()
    jobScheduler.schedule(job)
}

Java

public static final int MY_BACKGROUND_JOB = 0;
...
public static void scheduleJob(Context context) {
  JobScheduler js =
      (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
  JobInfo job = new JobInfo.Builder(
    MY_BACKGROUND_JOB,
    new ComponentName(context, MyJobService.class))
      .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)
      .setRequiresCharging(true)
      .build();
  js.schedule(job);
}

Saat kondisi tugas terpenuhi, aplikasi Anda akan menerima callback untuk menjalankan metode onStartJob() dalam JobService.class yang ditetapkan. Untuk melihat contoh penerapan JobScheduler, lihat aplikasi sampel JobScheduler.

Alternatif baru untuk JobScheduler adalah WorkManager, sebuah API yang memungkinkan Anda menjadwalkan tugas latar belakang yang membutuhkan penyelesaian terjamin, terlepas dari apakah proses aplikasi tersebut aktif atau tidak. WorkManager memilih cara yang tepat untuk menjalankan tugas tersebut (baik secara langsung di thread dalam proses aplikasi Anda maupun menggunakan JobScheduler, FirebaseJobDispatcher, atau AlarmManager) berdasarkan faktor-faktor seperti API level perangkat. Selain itu, WorkManager tidak memerlukan layanan Play dan menyediakan beberapa fitur lanjutan, seperti merangkai tugas bersama-sama atau memeriksa status tugas. Untuk mempelajari lebih lanjut, lihat WorkManager.

Memantau konektivitas jaringan selagi aplikasi berjalan

Aplikasi yang sedang berjalan masih dapat memproses CONNECTIVITY_CHANGE dengan BroadcastReceiver yang terdaftar. Namun, ConnectivityManager API menyediakan metode yang lebih andal untuk meminta callback hanya jika kondisi jaringan tertentu terpenuhi.

Objek NetworkRequest mendefinisikan parameter callback jaringan dari segi NetworkCapabilities. Anda membuat objek NetworkRequest dengan class NetworkRequest.Builder. Selanjutnya, registerNetworkCallback() meneruskan objek NetworkRequest ke sistem. Saat kondisi jaringan terpenuhi, aplikasi akan menerima callback untuk menjalankan metode onAvailable() yang ditentukan di class ConnectivityManager.NetworkCallback.

Aplikasi akan terus menerima callback hingga aplikasi keluar atau memanggil unregisterNetworkCallback().

Pembatasan penerimaan gambar dan siaran video

Di Android 7.0 (API level 24), aplikasi tidak dapat mengirim atau menerima siaran ACTION_NEW_PICTURE atau ACTION_NEW_VIDEO. Pembatasan ini membantu mengurangi dampak performa dan pengalaman pengguna saat beberapa aplikasi harus aktif untuk memproses gambar atau video baru. Android 7.0 (API level 24) memperluas JobInfo dan JobParameters untuk menyediakan solusi alternatif.

Memicu tugas saat URI konten berubah

Untuk memicu tugas saat URI konten berubah, Android 7.0 (API level 24) memperluas JobInfo API dengan metode berikut:

JobInfo.TriggerContentUri()
Melakukan enkapsulasi parameter yang diperlukan untuk memicu tugas saat URI konten berubah.
JobInfo.Builder.addTriggerContentUri()
Meneruskan objek TriggerContentUri ke JobInfo. ContentObserver akan memantau URI konten yang dienkapsulasi. Jika terdapat beberapa objek TriggerContentUri yang terkait dengan tugas, sistem akan memberikan callback meskipun perubahan yang dilaporkan hanya menyangkut salah satu URI konten.
Tambahkan flag TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS untuk memicu tugas jika ada turunan dari perubahan URI yang ditentukan. Flag ini berkaitan dengan parameter notifyForDescendants yang diteruskan ke registerContentObserver().

Catatan: TriggerContentUri() tidak dapat digunakan bersama setPeriodic() atau setPersisted(). Untuk terus memantau perubahan konten, jadwalkan JobInfo baru sebelum JobService aplikasi selesai menangani callback terbaru.

Kode contoh berikut menjadwalkan tugas yang akan dipicu saat sistem melaporkan perubahan pada URI konten, MEDIA_URI:

Kotlin

const val MY_BACKGROUND_JOB = 0
...
fun scheduleJob(context: Context) {
    val jobScheduler = context.getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler
    val job = JobInfo.Builder(
            MY_BACKGROUND_JOB,
            ComponentName(context, MediaContentJob::class.java)
    )
            .addTriggerContentUri(
                    JobInfo.TriggerContentUri(
                            MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                            JobInfo.TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS
                    )
            )
            .build()
    jobScheduler.schedule(job)
}

Java

public static final int MY_BACKGROUND_JOB = 0;
...
public static void scheduleJob(Context context) {
  JobScheduler js =
          (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
  JobInfo.Builder builder = new JobInfo.Builder(
          MY_BACKGROUND_JOB,
          new ComponentName(context, MediaContentJob.class));
  builder.addTriggerContentUri(
          new JobInfo.TriggerContentUri(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
          JobInfo.TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS));
  js.schedule(builder.build());
}

Saat sistem melaporkan perubahan dalam URI konten yang ditentukan, aplikasi Anda akan menerima callback dan objek JobParameters diteruskan ke metode onStartJob() dalam MediaContentJob.class.

Menentukan otoritas konten yang memicu tugas

Android 7.0 (API level 24) juga memperluas JobParameters agar aplikasi Anda dapat menerima informasi berguna tentang otoritas konten dan URI yang memicu tugas:

Uri[] getTriggeredContentUris()
Menampilkan array URI yang memicu tugas. Array ini akan bernilai null jika tidak ada URI yang memicu tugas tersebut (misalnya, tugas terpicu karena batas waktu atau alasan lain), atau jumlah URI yang berubah lebih dari 50.
String[] getTriggeredContentAuthorities()
Menampilkan array string otoritas konten yang memicu tugas. Jika array yang ditampilkan tidak bernilai null, gunakan getTriggeredContentUris() untuk mengambil detail URI mana yang telah berubah.

Kode contoh berikut mengganti metode JobService.onStartJob() dan mencatat otoritas konten dan URI yang memicu tugas:

Kotlin

override fun onStartJob(params: JobParameters): Boolean {
    StringBuilder().apply {
        append("Media content has changed:\n")
        params.triggeredContentAuthorities?.also { authorities ->
            append("Authorities: ${authorities.joinToString(", ")}\n")
            append(params.triggeredContentUris?.joinToString("\n"))
        } ?: append("(No content)")
        Log.i(TAG, toString())
    }
    return true
}

Java

@Override
public boolean onStartJob(JobParameters params) {
  StringBuilder sb = new StringBuilder();
  sb.append("Media content has changed:\n");
  if (params.getTriggeredContentAuthorities() != null) {
      sb.append("Authorities: ");
      boolean first = true;
      for (String auth :
          params.getTriggeredContentAuthorities()) {
          if (first) {
              first = false;
          } else {
             sb.append(", ");
          }
           sb.append(auth);
      }
      if (params.getTriggeredContentUris() != null) {
          for (Uri uri : params.getTriggeredContentUris()) {
              sb.append("\n");
              sb.append(uri);
          }
      }
  } else {
      sb.append("(No content)");
  }
  Log.i(TAG, sb.toString());
  return true;
}

Mengoptimalkan aplikasi Anda lebih jauh

Mengoptimalkan aplikasi agar berjalan di perangkat bermemori rendah, atau dalam kondisi memori rendah, dapat meningkatkan performa dan pengalaman pengguna. Menghapus dependensi pada layanan latar belakang dan penerima siaran implisit yang terdaftar di manifes dapat membuat aplikasi Anda berjalan lebih baik di perangkat tersebut. Meskipun Android 7.0 (API level 24) mengambil langkah untuk mengurangi sebagian masalah ini, sebaiknya Anda mengoptimalkan aplikasi agar berjalan tanpa menggunakan proses latar belakang ini sepenuhnya.

Perintah Android Debug Bridge (ADB) berikut dapat membantu Anda menguji perilaku aplikasi dengan proses latar belakang yang dinonaktifkan:

  • Untuk menyimulasikan kondisi saat siaran implisit dan layanan latar belakang tidak tersedia, masukkan perintah berikut:
  • $ adb shell cmd appops set <package_name> RUN_IN_BACKGROUND ignore
    
  • Untuk mengaktifkan kembali siaran implisit dan layanan latar belakang, masukkan perintah berikut:
  • $ adb shell cmd appops set <package_name> RUN_IN_BACKGROUND allow
    
  • Anda dapat melakukan simulasi pengguna yang menempatkan aplikasi Anda dalam status "dibatasi" untuk penggunaan baterai di latar belakang. Setelan ini mencegah aplikasi Anda berjalan di latar belakang. Untuk melakukannya, jalankan perintah berikut di jendela terminal:
  • $ adb shell cmd appops set <PACKAGE_NAME> RUN_ANY_IN_BACKGROUND deny