Apa hubungan antara Looper, Handler dan MessageQueue di Android?

95

Saya telah memeriksa dokumentasi / panduan resmi Android Looper, Handlerdan MessageQueue. Tapi saya tidak bisa mendapatkannya. Saya baru mengenal android, dan menjadi sangat bingung dengan konsep ini.

Blake
sumber

Jawaban:

103

A Looperadalah loop penanganan pesan: ia membaca dan memproses item dari a MessageQueue. The Looperkelas biasanya digunakan dalam hubungannya dengan HandlerThread(subclass dari Thread).

A Handleradalah kelas utilitas yang memfasilitasi interaksi dengan Looper—terutama dengan memposting pesan dan Runnableobjek ke utas MessageQueue. Ketika a Handlerdibuat, itu terikat ke tertentu Looper(dan utas terkait dan antrian pesan).

Dalam penggunaan umum, Anda membuat dan memulai HandlerThread, lalu membuat Handlerobjek (atau objek) yang dengannya utas lain dapat berinteraksi dengan HandlerThreadinstance. The Handlerharus dibuat sementara berjalan pada HandlerThread, meskipun sekali dibuat tidak ada pembatasan pada apa benang dapat menggunakan Handler's metode penjadwalan ( post(Runnable), dll)

Utas utama (alias utas UI) dalam aplikasi Android disiapkan sebagai utas penangan sebelum instance aplikasi Anda dibuat.

Selain dokumen kelas, ada diskusi bagus tentang semua ini di sini .

PS Semua kelas yang disebutkan di atas ada di dalam paket android.os.

Ted Hopp
sumber
@Ted Hopp - Apakah antrean pesan Looper berbeda dari antrean pesan Thread?
CopsOnRoad
2
@ Jack - Mereka adalah hal yang sama. DokumentasiMessageQueue Android API menyatakan bahwa a MessageQueueadalah " kelas tingkat rendah yang menyimpan daftar pesan yang akan dikirim oleh Looper. "
Ted Hopp
95

Telah diketahui secara luas bahwa memperbarui komponen UI langsung dari utas selain utas utama di android adalah ilegal . Dokumen android ini ( Menangani Operasi yang Mahal di UI Thread ) menyarankan langkah-langkah yang harus diikuti jika kita perlu memulai thread terpisah untuk melakukan pekerjaan yang mahal dan memperbarui UI setelah selesai. Idenya adalah untuk membuat objek Handler yang terkait dengan utas utama , dan memposting Runnable padanya pada waktu yang tepat. Ini Runnableakan dipanggil di utas utama . Mekanisme ini diimplementasikan dengan kelas Looper dan Handler .

The Looperkelas mempertahankan MessageQueue , yang berisi daftar pesan . Karakter penting Looper adalah bahwa ini terkait dengan utas tempat Looperdibuatnya . Asosiasi ini disimpan selamanya dan tidak dapat dipatahkan atau diubah. Perhatikan juga bahwa utas tidak dapat dikaitkan dengan lebih dari satu Looper. Untuk menjamin pengaitan ini, Looperdisimpan dalam penyimpanan lokal-thread, dan tidak dapat dibuat melalui konstruktornya secara langsung. Satu-satunya cara untuk membuatnya adalah dengan memanggil siapkan metode statis Looper. persiapkan metode pertama memeriksa ThreadLocaldari utas saat ini untuk memastikan bahwa belum ada Looper yang terkait dengan utas tersebut. Setelah pemeriksaan, yang baru Looperdibuat dan disimpan di ThreadLocal. Setelah menyiapkan Looper, kita dapat memanggil metode loop untuk memeriksa pesan baru dan harus Handlermenanganinya.

Seperti namanya, Handlerkelas ini terutama bertanggung jawab untuk menangani (menambah, menghapus, mengirim) pesan dari utas saat ini MessageQueue. Sebuah Handlerinstance juga terikat ke utas. The mengikat antara Handler dan Thread dicapai melalui Looperdan MessageQueue. Sebuah Handlertersebut selalu terikat sebuah Looper, dan kemudian terikat pada benang terkait dengan Looper. Tidak seperti Looper, beberapa contoh Handler dapat diikat ke utas yang sama. Setiap kali kita memanggil post atau metode serupa di Handler, pesan baru ditambahkan ke terkait MessageQueue. Bidang target pesan disetel ke Handlercontoh saat ini . KetikaLoopermenerima pesan ini, ia memanggil dispatchMessage pada bidang target pesan, sehingga pesan tersebut dirutekan kembali ke contoh Handler untuk ditangani, tetapi pada utas yang benar. Hubungan antara Looper, Handlerdan MessageQueueditampilkan di bawah ini:

masukkan deskripsi gambar di sini

K_Anas
sumber
5
Terima kasih! tetapi apa gunanya jika penangan pertama kali memposting pesan ke antrian pesan dan kemudian menangani pesan dari antrian yang sama? mengapa tidak menangani pesan secara langsung?
Blake
4
@Blake b / c Anda memposting dari satu utas (utas non-looper) tetapi menangani pesan di utas lain (utas looper)
numan salati
Jauh lebih baik daripada yang didokumentasikan di developer.android.com - tetapi alangkah baiknya melihat kode untuk diagram yang Anda berikan.
tfmontague
@numansalati - Tidak dapat Penangan memposting pesan dari utas looper?
CopsOnRoad
78

Mari kita mulai dengan Looper. Anda dapat memahami hubungan antara Looper, Handler, dan MessageQueue dengan lebih mudah jika Anda memahami apa itu Looper. Anda juga dapat lebih memahami apa itu Looper dalam konteks kerangka GUI. Looper dibuat untuk melakukan 2 hal.

1) Looper mengubah utas normal , yang berhenti ketika run()metodenya kembali, menjadi sesuatu yang berjalan terus menerus hingga aplikasi Android berjalan , yang diperlukan dalam kerangka GUI (Secara teknis, ini masih berakhir ketika run()metode kembali. Tapi izinkan saya menjelaskan apa yang saya maksud, di bawah).

2) Looper menyediakan antrian di mana pekerjaan yang harus diselesaikan diantrekan, yang juga diperlukan dalam kerangka GUI.

Seperti yang Anda ketahui, saat aplikasi diluncurkan, sistem membuat thread eksekusi untuk aplikasi tersebut, yang disebut "main", dan aplikasi Android biasanya berjalan sepenuhnya pada satu thread secara default sebagai "thread utama". Tapi utas utama bukanlah utas rahasia, utas khusus . Itu hanya utas normal yang juga dapat Anda buat dengan new Thread()kode, yang berarti itu berakhir ketika run()metodenya kembali! Pikirkan contoh di bawah ini.

public class HelloRunnable implements Runnable {
    public void run() {
        System.out.println("Hello from a thread!");
    }

    public static void main(String args[]) {
        (new Thread(new HelloRunnable())).start();
    }
}

Sekarang, mari terapkan prinsip sederhana ini ke aplikasi Android. Apa yang akan terjadi jika aplikasi Android dijalankan di thread normal? Sebuah utas bernama "main" atau "UI" atau apa pun yang memulai aplikasi, dan menggambar semua UI. Jadi, layar pertama ditampilkan kepada pengguna. Jadi bagaimana sekarang? Utas utama berakhir? Tidak, seharusnya tidak. Itu harus menunggu sampai pengguna melakukan sesuatu, bukan? Tetapi bagaimana kita bisa mencapai perilaku ini? Nah, kita bisa coba dengan Object.wait()atauThread.sleep(). Misalnya, utas utama menyelesaikan tugas awalnya untuk menampilkan layar pertama, dan tidur. Itu bangun, yang berarti terputus, ketika pekerjaan baru yang harus dilakukan diambil. Sejauh ini bagus, tapi saat ini kita membutuhkan struktur data seperti antrian untuk menampung banyak pekerjaan. Pikirkan tentang kasus ketika pengguna menyentuh layar secara berurutan, dan tugas membutuhkan waktu lebih lama untuk menyelesaikannya. Jadi, kita perlu memiliki struktur data untuk menampung pekerjaan yang harus dilakukan dengan cara pertama masuk pertama keluar. Selain itu, Anda mungkin membayangkan, mengimplementasikan thread yang selalu-berjalan-dan-proses-pekerjaan-ketika-tiba menggunakan interupsi tidaklah mudah, dan mengarah ke kode yang kompleks dan seringkali tidak dapat dikelola. Kami lebih suka membuat mekanisme baru untuk tujuan seperti itu, dan itulah Looper . The dokumen resmi kelas Loopermengatakan, "Thread secara default tidak memiliki loop pesan yang terkait dengannya", dan Looper adalah kelas "yang digunakan untuk menjalankan loop pesan untuk thread". Sekarang Anda bisa mengerti artinya.

Mari pindah ke Handler dan MessageQueue. Pertama, MessageQueue adalah antrian yang saya sebutkan di atas. Itu berada di dalam Looper, dan hanya itu. Anda dapat memeriksanya dengan kode sumber kelas Looper . Kelas Looper memiliki variabel anggota MessageQueue.

Lalu, apakah Handler itu? Jika ada antrian, maka harus ada metode yang memungkinkan kita memasukkan tugas baru ke antrian, bukan? Itulah yang dilakukan Handler. Kita bisa memasukkan tugas baru ke antrean (MessageQueue) menggunakan berbagai post(Runnable r)metode. Itu dia. Ini semua tentang Looper, Handler, dan MessageQueue.

Kata terakhir saya adalah, jadi pada dasarnya Looper adalah class yang dibuat untuk mengatasi masalah yang terjadi pada framework GUI. Tetapi kebutuhan semacam ini juga dapat terjadi dalam situasi lain. Sebenarnya ini adalah pola yang cukup terkenal untuk aplikasi multi utas, dan Anda dapat mempelajarinya lebih lanjut di "Pemrograman Bersamaan di Java" oleh Doug Lea (Terutama, bab 4.1.4 "Utas Pekerja" akan sangat membantu). Selain itu, Anda dapat membayangkan mekanisme semacam ini tidak unik dalam kerangka kerja Android, tetapi semua kerangka kerja GUI mungkin perlu serupa dengan ini. Anda dapat menemukan mekanisme yang hampir sama dalam kerangka Java Swing.

김준호
sumber
4
Jawaban Terbaik. Pelajari lebih lanjut dari penjelasan rinci ini. Saya ingin tahu apakah ada beberapa posting blog yang lebih detail.
capt.swag
Bisakah pesan ditambahkan ke MessageQueue tanpa menggunakan Handler?
CopsOnRoad
@CopsOnRoad tidak, mereka tidak dapat ditambahkan secara langsung.
Faisal Naseer
Membuat hari saya ... banyak cinta untuk Anda :)
Rahul Matte
26

MessageQueue: Ini adalah kelas tingkat rendah yang menyimpan daftar pesan yang akan dikirim oleh a Looper. Pesan tidak ditambahkan secara langsung ke a MessageQueue, melainkan melalui Handlerobjek yang terkait dengan Looper. [ 3 ]

Looper: Ini loop di atas MessageQueueyang berisi pesan yang akan dikirim. Tugas sebenarnya dari mengatur antrian dilakukan oleh Handleryang bertanggung jawab untuk menangani (menambah, menghapus, mengirim) pesan dalam antrian pesan. [ 2 ]

Handler: Ini memungkinkan Anda untuk mengirim dan memproses Messagedan Runnableobjek yang terkait dengan utas MessageQueue. Setiap contoh Handler dikaitkan dengan satu utas dan antrian pesan utas itu. [ 4 ]

Ketika Anda membuat yang baru Handler, itu terikat ke utas / antrian pesan dari utas yang membuatnya - sejak saat itu , itu akan mengirimkan pesan dan runnable ke antrian pesan itu dan mengeksekusinya saat mereka keluar dari antrian pesan .

Mohon, lihat gambar di bawah [ 2 ] untuk pemahaman yang lebih baik.

masukkan deskripsi gambar di sini

AnV
sumber
0

Memperluas jawabannya, oleh @K_Anas, dengan contoh, Seperti yang dinyatakan

Telah diketahui secara luas bahwa memperbarui komponen UI langsung dari utas selain utas utama di android adalah ilegal.

misalnya jika Anda mencoba memperbarui UI menggunakan Thread.

    int count = 0;
    new Thread(new Runnable(){
        @Override
        public void run() {
            try {
                while(true) {
                    sleep(1000);
                    count++;
                    textView.setText(String.valueOf(count));
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }


   ).start();

aplikasi Anda akan mogok dengan pengecualian.

android.view.ViewRoot $ CalledFromWrongThreadException: Hanya utas asli yang membuat hierarki tampilan yang dapat menyentuh tampilannya.

dengan kata lain Anda perlu menggunakan Handleryang tetap mengacu pada tugas MainLooper ie Main Threadatau UI Threaddan lulus sebagai Runnable.

  Handler handler = new Handler(getApplicationContext().getMainLooper);
        int count = 0;
        new Thread(new Runnable(){
            @Override
            public void run() {
                try {
                    while(true) {
                        sleep(1000);
                        count++;
                        handler.post(new Runnable() {
                           @Override
                           public void run() {
                                 textView.setText(String.valueOf(count));
                           }
                         });

                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

    ).start() ;
Faisal Naseer
sumber