Jika komponen aplikasi dimulai dan tidak ada komponen aplikasi 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 (disebut thread "utama"). Jika komponen aplikasi dimulai dan sudah ada proses untuk aplikasi itu (karena komponen lain dari aplikasi itu sudah ada), maka komponen akan dimulai dalam proses itu dan menggunakan thread eksekusi yang sama. Akan tetapi, Anda bisa mengatur komponen berbeda di aplikasi agar berjalan di proses terpisah, dan Anda bisa membuat thread tambahan untuk setiap proses.
Dokumen ini membahas cara kerja proses dan thread di aplikasi Android.
Proses
Secara default, semua komponen aplikasi yang sama berjalan dalam proses yang sama dan kebanyakan aplikasi tidak boleh mengubah ini. Akan tetapi, jika Anda merasa perlu mengontrol proses milik komponen tertentu, Anda dapat melakukannya dalam file manifes.
Entri manifes untuk setiap tipe elemen komponen—<activity>
, <service>
, <receiver>
, dan <provider>
—mendukung atribut android:process
yang bisa menetapkan dalam proses mana komponen harus dijalankan. Anda bisa mengatur atribut ini agar setiap komponen berjalan dalam prosesnya sendiri atau agar beberapa komponen menggunakan proses yang sama sementara yang lainnya tidak. Anda juga bisa menyetel android:process
agar komponen aplikasi yang berbeda berjalan dalam proses yang sama—sepanjang aplikasi menggunakan ID pengguna Linux yang sama dan ditandatangani dengan sertifikat yang sama.
Elemen <application>
juga mendukung atribut android:process
, untuk menyetel nilai default yang berlaku bagi semua komponen.
Android bisa memutuskan untuk mematikan proses pada waktu tertentu, jika memori tinggal sedikit dan diperlukan oleh proses lain yang lebih mendesak untuk melayani pengguna. Komponen aplikasi yang berjalan dalam proses yang dimatikan konsekuensinya juga akan dimusnahkan. Proses dimulai kembali untuk komponen tersebut jika ada lagi pekerjaan untuk mereka lakukan.
Saat memutuskan proses yang akan dimatikan, sistem Android akan mempertimbangkan kepentingan relatifnya bagi pengguna. Misalnya, sistem lebih mudah menghentikan proses yang menjadi host aktivitas yang tidak lagi terlihat di layar, dibandingkan dengan proses yang menjadi host aktivitas yang terlihat. Karena itu, keputusan untuk menghentikan proses bergantung pada status komponen yang berjalan dalam proses tersebut.
Detail daur hidup proses dan hubungannya dengan status aplikasi dibahas dalam Daur Hidup Proses dan Aplikasi.
Thread
Jika aplikasi diluncurkan, sistem akan membuat thread eksekusi untuk aplikasi tersebut, yang diberi nama, "main". Thread ini sangat penting karena bertugas mengirim kejadian ke widget antarmuka pengguna yang sesuai, termasuk peristiwa menggambar. Ini juga hampir selalu merupakan thread yang membuat aplikasi berinteraksi dengan komponen dari toolkit Android UI (komponen dari paket android.widget
dan android.view
). Karena itu, thread 'main' juga terkadang disebut thread UI. Namun, pada situasi tertentu, thread utama aplikasi mungkin bukan thread UI-nya; untuk informasi selengkapnya, lihat Anotasi Thread.
Sistem ini tidak membuat thread terpisah untuk setiap instance komponen. Semua komponen yang berjalan di proses yang sama akan dibuat instance-nya dalam thread UI, dan sistem akan memanggil setiap komponen yang dikirim dari thread itu. Akibatnya, metode yang merespons callback sistem (seperti onKeyDown()
untuk melaporkan tindakan pengguna atau metode callback daur hidup) selalu berjalan di thread UI proses.
Misalnya saat pengguna menyentuh tombol pada layar, thread UI aplikasi akan mengirim peristiwa sentuh ke widget, yang selanjutnya menetapkan status ditekan dan memposting permintaan yang tidak divalidasi ke antrean peristiwa. Thread UI akan menghapus antrean permintaan dan memberi tahu widget bahwa widget harus menggambar ulang dirinya sendiri.
Saat aplikasi melakukan pekerjaan intensif sebagai respons terhadap interaksi pengguna, model thread tunggal ini bisa menghasilkan kinerja yang buruk kecuali jika Anda mengimplementasikan aplikasi dengan benar. Khususnya jika semua terjadi di thread UI, melakukan operasi yang panjang seperti akses ke jaringan atau kueri database akan memblokir seluruh UI. Jika thread diblokir, tidak ada peristiwa yang bisa dikirim, termasuk peristiwa menggambar. Dari sudut pandang pengguna, aplikasi seperti hang atau mogok. Lebih buruk lagi, jika thread UI diblokir selama lebih dari beberapa detik (saat ini sekitar 5 detik) pengguna akan ditampilkan dialog "aplikasi tidak merespons" (ANR) yang populer karena reputasi buruknya. Pengguna nanti bisa memutuskan untuk keluar dari aplikasi dan menghapus aplikasi jika tidak suka.
Selain itu, toolkit Android UI bukan thread-safe. Jadi, Anda tidak harus memanipulasi UI dari thread pekerja—Anda harus melakukan semua manipulasi pada antarmuka pengguna dari thread UI. Sehingga hanya ada dua aturan untuk model thread tunggal Android:
- Jangan memblokir thread UI
- Jangan mengakses toolkit Android UI dari luar thread UI
Thread pekerja
Karena model thread tunggal yang dijelaskan di atas, Anda dilarang memblokir thread UI demi daya respons UI aplikasi. Jika memiliki operasi untuk dijalankan yang tidak seketika, Anda harus memastikan untuk melakukannya di thread terpisah (thread "latar belakang" atau thread "pekerja").
Namun, ingat bahwa Anda tidak dapat memperbarui UI dari thread selain dari thread UI atau thread "utama".
Untuk memperbaiki masalah ini, Android menawarkan beberapa cara untuk mengakses thread UI dari thread lainnya. Berikut ini daftar metode yang bisa membantu:
Kotlin
fun onClick(v: View) { Thread(Runnable { // a potentially time consuming task val bitmap = processBitMap("image.png") imageView.post { imageView.setImageBitmap(bitmap) } }).start() }
Java
public void onClick(View v) { new Thread(new Runnable() { public void run() { // a potentially time consuming task final Bitmap bitmap = processBitMap("image.png"); imageView.post(new Runnable() { public void run() { imageView.setImageBitmap(bitmap); } }); } }).start(); }
Kini implementasi ini thread-safe: operasi latar belakang dilakukan dari thread terpisah sementara ImageView
selalu dimanipulasi dari thread UI.
Akan tetapi, karena operasi semakin kompleks, jenis kode seperti ini bisa semakin rumit dan sulit dipertahankan. Untuk menangani interaksi yang lebih kompleks dengan thread pekerja, Anda bisa mempertimbangkan penggunaan Handler
di thread pekerja, untuk memproses pesan yang dikirim dari thread UI. Mungkin solusi terbaiknya adalah memperpanjang kelas AsyncTask
, yang akan menyederhanakan eksekusi tugas-tugas thread pekerja yang perlu berinteraksi dengan UI.
Menggunakan AsyncTask
Dengan AsyncTask
, memungkinkan Anda melakukan pekerjaan asinkron pada antarmuka pengguna. Hal ini memblokir operasi di thread pekerja kemudian mempublikasikan hasilnya di thread UI, tanpa mengharuskan Anda untuk menangani sendiri thread dan/atau penangan sendiri.
Untuk menggunakannya, Anda harus menjadikan AsyncTask
sebagai subkelas dan mengimplementasikan metode callback doInBackground()
yang berjalan di pool thread latar belakang. Untuk memperbarui UI, Anda harus mengimplementasikan onPostExecute()
, yang mengirim hasil dari doInBackground()
dan berjalan di thread UI, jadi Anda bisa memperbarui UI dengan aman. Kemudian Anda bisa menjalankan tugas dengan memanggil execute()
dari thread UI.
Anda harus membaca referensi AsyncTask
untuk memahami sepenuhnya cara menggunakan kelas ini.
Metode thread-safe
Dalam beberapa situasi, metode yang Anda implementasikan bisa dipanggil dari lebih dari satu thread, dan karena itu harus ditulis agar menjadi thread-safe.
Ini terutama terjadi untuk metode yang bisa dipanggil dari jauh—seperti metode dalam layanan terikat. Jika sebuah panggilan pada metode yang diimplementasikan pada IBinder
berasal dari proses yang sama di mana IBinder
berjalan, metode akan dijalankan di thread pemanggil. Namun, jika panggilan berasal proses lain, metode akan dieksekusi dalam thread yang dipilih dari kumpulan (pool) thread yang dipertahankan sistem dalam proses yang sama seperti IBinder
(tidak dijalankan dalam thread UI proses). Misalnya, karena metode onBind()
layanan akan dipanggil dari thread UI proses layanan, metode yang diimplementasikan dalam objek yang dikembalikan onBind()
(misalnya, subclass yang mengimplementasikan metode RPC) akan dipanggil dari thread dalam pool. Karena layanan bisa memiliki lebih dari satu klien, maka lebih dari satu pool thread bisa melibatkan metode IBinder
yang sama sekaligus. Metode IBinder
karenanya harus diimplementasikan sebagai thread-safe.
Demikian pula, penyedia materi bisa menerima permintaan data yang berasal dari proses lain. Meskipun class ContentResolver
dan ContentProvider
menyembunyikan detail cara mengelola komunikasi antarproses, metode ContentProvider
yang merespons permintaan itu—metode query()
, insert()
, delete()
, update()
, dan getType()
—dipanggil dari pool thread pada proses penyedia materi, bukan thread UI untuk proses tersebut. Mengingat metode ini bisa dipanggil dari thread mana pun sekaligus, metode-metode ini juga harus diimplementasikan sebagai thread-safe.
Komunikasi antarproses
Android menawarkan mekanisme komunikasi antarproses (IPC) menggunakan panggilan prosedur jauh (PPK), yang mana metode ini dipanggil oleh aktivitas atau komponen aplikasi lain, tetapi dieksekusi dari jauh (di proses lain), bersama hasil yang dikembalikan ke pemanggil. Ini mengharuskan penguraian panggilan metode dan datanya ke tingkat yang bisa dipahami sistem operasi, mentransmisikannya dari proses lokal dan ruang alamat untuk proses jauh dan ruang proses, kemudian merakit kembali dan menetapkannya kembali di sana. Nilai-nilai yang dikembalikan akan ditransmisikan dalam arah berlawanan. Android menyediakan semua kode untuk melakukan transaksi IPC ini, sehingga Anda bisa fokus pada pendefinisian dan implementasi antarmuka pemrograman PPK.
Untuk melakukan IPC, aplikasi Anda harus ditautkan ke layanan, menggunakan bindService()
. Untuk informasi selengkapnya, lihat panduan developer Layanan.