Ringkasan proses dan thread

Saat komponen aplikasi dimulai dan aplikasi tidak memiliki komponen lain yang berjalan, sistem Android akan memulai proses Linux baru untuk aplikasi dengan satu thread eksekusi. Secara default, semua komponen aplikasi yang sama berjalan dalam proses dan thread yang sama, yang disebut thread utama.

Jika komponen aplikasi dimulai dan sudah ada proses untuk aplikasi tersebut, karena komponen lain dari aplikasi sudah dimulai, maka komponen dimulai dalam proses tersebut dan menggunakan thread eksekusi yang sama. Namun, Anda dapat mengatur agar berbagai komponen dalam aplikasi Anda berjalan dalam proses terpisah, dan Anda dapat membuat thread tambahan untuk proses apa pun.

Dokumen ini membahas cara kerja proses dan thread dalam aplikasi Android.

Proses

Secara default, semua komponen aplikasi berjalan dalam proses yang sama, dan sebagian besar aplikasi tidak mengubahnya. Namun, jika Anda merasa perlu mengontrol proses mana yang menjadi bagian dari komponen tertentu, Anda dapat melakukannya di file manifes.

Entri manifes untuk setiap jenis elemen komponen—<activity>, <service>, <receiver>, dan <provider>—mendukung atribut android:process yang dapat menentukan proses yang dijalankan komponen. Anda dapat menetapkan atribut ini sehingga setiap komponen berjalan dalam prosesnya sendiri atau sehingga beberapa komponen berbagi proses sementara yang lain tidak.

Anda juga dapat menetapkan android:process sehingga komponen dari aplikasi yang berbeda berjalan dalam proses yang sama, asalkan aplikasi tersebut berbagi ID pengguna Linux yang sama dan ditandatangani dengan sertifikat yang sama.

Elemen <application> juga mendukung atribut android:process, yang dapat Anda gunakan untuk menetapkan nilai default yang berlaku untuk semua komponen.

Android dapat memutuskan untuk mematikan proses pada suatu saat, saat resource diperlukan oleh proses lain yang lebih langsung melayani pengguna. Komponen aplikasi yang berjalan dalam proses yang dihentikan akan dihancurkan. Proses dimulai lagi untuk komponen tersebut saat ada tugas yang harus dilakukan.

Saat memutuskan proses mana yang harus dihentikan, sistem Android mempertimbangkan kepentingan relatifnya bagi pengguna. Misalnya, proses yang menghosting aktivitas yang tidak lagi terlihat di layar akan lebih mudah dihentikan, dibandingkan dengan proses yang menghosting aktivitas yang terlihat. Oleh karena itu, keputusan untuk menghentikan proses bergantung pada status komponen yang berjalan dalam proses tersebut.

Detail siklus proses dan hubungannya dengan status aplikasi dibahas dalam Proses dan siklus proses aplikasi.

Rangkaian pesan

Saat aplikasi diluncurkan, sistem akan membuat thread eksekusi untuk aplikasi tersebut, yang disebut thread utama. Thread ini sangat penting, karena bertugas mengirimkan peristiwa ke widget antarmuka pengguna yang sesuai, termasuk peristiwa menggambar. Thread ini juga hampir selalu menjadi thread tempat aplikasi Anda berinteraksi dengan komponen dari paket android.widget dan android.view Android UI toolkit. Oleh karena itu, thread utama terkadang disebut sebagai thread UI. Namun, dalam keadaan khusus, thread utama aplikasi mungkin bukan thread UI-nya. Untuk mengetahui informasi selengkapnya, lihat Anotasi thread.

Sistem tidak membuat thread terpisah untuk setiap instance komponen. Semua komponen yang berjalan dalam proses yang sama di-instance dalam thread UI, dan panggilan sistem ke setiap komponen dikirim dari thread tersebut. Oleh karena itu, metode yang merespons callback sistem—seperti onKeyDown() untuk melaporkan tindakan pengguna, atau metode callback siklus proses—selalu berjalan di UI thread proses.

Misalnya, saat pengguna menyentuh tombol di layar, UI thread aplikasi Anda akan mengirimkan peristiwa sentuh ke widget, yang pada gilirannya menetapkan status ditekan dan memposting permintaan invalidasi ke antrean peristiwa. UI thread mengeluarkan permintaan dari antrean dan memberi tahu widget untuk menggambar ulang dirinya sendiri.

Kecuali jika Anda menerapkan aplikasi dengan benar, model satu thread ini dapat menghasilkan performa yang buruk saat aplikasi Anda melakukan pekerjaan berat sebagai respons terhadap interaksi pengguna. Melakukan operasi yang lama di UI thread, seperti akses jaringan atau kueri database, akan memblokir seluruh UI. Jika thread diblokir, tidak ada peristiwa yang dapat dikirim, termasuk peristiwa gambar.

Dari sudut pandang pengguna, aplikasi tampak berhenti merespons. Lebih buruk lagi, jika UI thread diblokir selama lebih dari beberapa detik, pengguna akan melihat dialog "aplikasi tidak merespons" (ANR). Pengguna kemudian dapat memutuskan untuk keluar dari aplikasi Anda atau bahkan meng-uninstal aplikasi tersebut.

Perlu diingat bahwa toolkit UI Android tidak aman untuk diakses dari beberapa thread. Jadi, jangan memanipulasi UI Anda dari thread pekerja. Lakukan semua manipulasi pada antarmuka pengguna dari thread UI. Ada dua aturan untuk model thread tunggal Android:

  1. Jangan memblokir thread UI.
  2. Jangan mengakses toolkit UI Android dari luar thread UI.

Thread pekerja

Karena model satu thread ini, sangat penting untuk responsivitas UI aplikasi Anda agar Anda tidak memblokir UI thread. Jika Anda memiliki operasi yang tidak instan, pastikan untuk melakukannya di thread latar belakang atau pekerja yang terpisah. Ingat saja bahwa Anda tidak dapat mengupdate UI dari thread selain thread UI, atau thread utama.

Untuk membantu Anda mematuhi aturan ini, Android menawarkan beberapa cara untuk mengakses thread UI dari thread lain. Berikut adalah daftar metode yang dapat membantu:

Contoh berikut menunjukkan cara mengalihkan tugas ke thread latar belakang dan mengupdate thread UI setelah tugas selesai:

Kotlin

// Kotlin coroutines implementation.
fun onClick(v: View) {
    // Launch a coroutine in the lifecycle scope (e.g., in an Activity or Fragment).
    lifecycleScope.launch {
        // Run the blocking task on the IO dispatcher.
        val bitmap = withContext(Dispatchers.IO) {
            BitmapFactory.decodeFile("image.png")
        }
        // Back on the main thread, update the UI.
        imageView.setImageBitmap(bitmap)
    }
}

Java

// Java Executor implementation.
// (executorService is assumed to be defined elsewhere).
public void onClick(View v) {
    executorService.execute(() -> {
        // Run the heavy task on a background thread.
        Bitmap bitmap = BitmapFactory.decodeFile("image.png");

        // Update the View on the UI thread.
        imageView.post(() -> imageView.setImageBitmap(bitmap));
    });
}

Implementasi ini aman untuk thread, karena operasi latar belakang dilakukan dari thread terpisah sementara ImageView selalu dimanipulasi dari UI thread.

Namun, seiring bertambahnya kompleksitas operasi, jenis kode ini dapat menjadi rumit dan sulit dikelola. Untuk menangani interaksi yang lebih kompleks dengan thread pekerja, Anda dapat mempertimbangkan untuk menggunakan Handler di thread pekerja untuk memproses pesan yang dikirim dari UI thread. Untuk penjelasan lengkap tentang cara menjadwalkan pekerjaan di thread latar belakang dan berkomunikasi kembali ke thread UI, lihat Ringkasan Pekerjaan Latar Belakang.

Metode yang aman untuk thread

Dalam beberapa situasi, metode yang Anda terapkan dipanggil dari lebih dari satu thread, dan oleh karena itu harus ditulis agar aman untuk thread.

Hal ini terutama berlaku untuk metode yang dapat dipanggil dari jarak jauh, seperti metode dalam layanan terikat. Saat panggilan pada metode yang diterapkan di IBinder berasal dari proses yang sama dengan tempat IBinder berjalan, metode akan dieksekusi di thread pemanggil. Namun, saat panggilan berasal dari proses lain, metode dieksekusi dalam thread yang dipilih dari kumpulan thread yang dikelola sistem dalam proses yang sama dengan IBinder. Tidak dijalankan di thread UI proses.

Misalnya, saat metode onBind() layanan dipanggil dari UI thread proses layanan, metode yang diterapkan dalam objek yang onBind() ditampilkan, seperti subclass yang menerapkan metode remote procedure call (RPC), dipanggil dari thread dalam pool. Karena layanan dapat memiliki lebih dari satu klien, lebih dari satu thread kumpulan dapat menggunakan metode IBinder yang sama secara bersamaan, sehingga metode IBinder harus diimplementasikan agar aman untuk thread.

Demikian pula, penyedia konten dapat menerima permintaan data yang berasal dari proses lain. Class ContentResolver dan ContentProvider menyembunyikan detail cara komunikasi antar-proses (IPC) dikelola, tetapi metode ContentProvider yang merespons permintaan tersebut—metode query(), insert(), delete(), update(), dan getType()—dipanggil dari kumpulan thread dalam proses penyedia konten, bukan thread UI untuk proses tersebut. Karena metode ini dapat dipanggil dari sejumlah thread secara bersamaan, metode ini juga harus diimplementasikan agar aman untuk thread.

Komunikasi antar-proses

Android menawarkan mekanisme untuk IPC menggunakan RPC, yang memungkinkan metode dipanggil oleh aktivitas atau komponen aplikasi lain, tetapi dieksekusi dari jarak jauh dalam proses lain, dengan hasil apa pun yang dikembalikan ke pemanggil. Hal ini memerlukan dekomposisi panggilan metode dan datanya ke tingkat yang dapat dipahami sistem operasi, mentransmisikannya dari proses dan ruang alamat lokal ke proses dan ruang alamat jarak jauh, lalu menyusun ulang dan mengulang panggilan di sana.

Nilai yang ditampilkan kemudian ditransmisikan ke arah yang berlawanan. Android menyediakan semua kode untuk melakukan transaksi IPC ini, sehingga Anda dapat berfokus pada penentuan dan penerapan antarmuka pemrograman RPC.

Untuk melakukan IPC, aplikasi Anda harus terikat ke layanan menggunakan bindService(). Untuk mengetahui informasi selengkapnya, lihat Ringkasan layanan.