Handler與Looper,MessageQueue的關系


       總結一下Handler與Looper,MessageQueue的關系,並實現自定義與子線程相關的Handler。

一、Handler與Looper,MessageQueue的關系

     它們之間的關系其實就是下面的三條說明:

(1)Looper:相當於消息的載體

     • 它的內部有一個消息隊列,也就是MessageQueue,Handler發送的所有消息都會走向這個消息隊里。

     • 它的Looper.loop方法是一個死循環,不斷的從消息隊列MessageQueue中取出消息。如果有消息存在就處理該消息,否則就阻塞。

(2)MessageQue:就是一個消息隊列,可以向其中添加消息並處理消息。

(3)Handler其實就是發送消息處理消息的封裝。它與Looper相關聯,也就是說在Handler的內部可以找到Looper,找到了Looper就找到了相應的消息隊列。因此Handler發送的消息都會走向MessageQueue。

      對上面三點說明做一個總結,其實就是:

Handler負責發送消息和接收Looper傳過來的消息,並根據消息處理相應邏輯
Looper負責接收Handler發送過來的消息,並將該消息回傳給Handler自己。
而MessageQueue只是相當於一個消息容器

     經過上面的說明,對android中的Handler消息處理機制應該有了一個大致的了解了。那么你肯定還有疑問,為什么處理消息要嚴格這樣子做呢?這個問題其實就是為什么不能在子線程中更新UI的問題。下面就是原因。

      為什么不能在子線程中更新UI

     我們假設可以在子線程更新UI,那么就會出現這樣子的一種況:其中一個子線程更新界面還沒有完成,另外一個子線程就又去更新UI了,這樣子會造成子UI界面錯亂。那么你可能會說,我們可以實行加鎖機制啊,讓更新UI的代碼不能並發執行。如果每一個子線程都加鎖,那么毫無疑問程序的性能將會大大下降。因此主要的原因總結起來就是兩點:

(1)為了防止界面錯亂

(2)為了防止程序性能下降

     因此android不允許在子線程中更新UI。為了解決這個這個問題,android就設計出這樣子的一套消息處理機制,讓我們盡管在子線程中發送更新UI的消息,而不用去關心多線程問題。在主線程的消息隊里中采取輪詢更新處理。

二、自定義與子線程關聯的Handler

      了解了Handler與Looper,MessageQueue后,我們可以實現自定義的Handler,該Handler與子線程關聯(注意我們創建的默認Handler都是與主線程,即UI線程關聯的,因為沒有指定Looper,因此如果Looper不存在是不能創建Handler的)。下面我們就來簡單實現這個自定義的Handler。新建一個項目,只需要修改它的MainActiivty代碼即可,如下:

 1 package com.example.handldertest;
 2 
 3 import android.app.Activity;
 4 import android.os.Bundle;
 5 import android.os.Handler;
 6 import android.os.Looper;
 7 import android.util.Log;
 8 import android.widget.TextView;
 9 
10 public class ThreadHandlerActivity extends Activity{
11     
12 
13     //創建子線程
14     class MyThread extends Thread{
15         private Handler handler;//與該子線程關聯的Handler    
16         public void run() {
17          
18             Looper.prepare();//創建該子線程的Looper
19             handler = new Handler(){//默認與當前線程關聯
20                 public void handleMessage(android.os.Message msg) {
21                     Log.d("當前子線程是----->", Thread.currentThread()+"");
22                 };
23             };
24             
25             Looper.loop();//只要調用了該方法才能不斷循環取出消息
26         }
27     }
28     
29     private TextView tv;
30     private MyThread thread;
31     
32     private Handler mHandler = new Handler(){
33         public void handleMessage(android.os.Message msg) {
34             Log.d("當前主線程是----->", Thread.currentThread()+"");
35         };
36     };
37     
38     protected void onCreate(Bundle savedInstanceState) {
39             super.onCreate(savedInstanceState);
40             tv = new TextView(this);
41             tv.setText("Handler實驗");
42             setContentView(tv);
43             
44             thread = new MyThread();
45             thread.start();//千萬別忘記開啟這個線程
46             try {
47                 Thread.sleep(2000);//主線程休眠2秒
48             } catch (InterruptedException e) {
49                 e.printStackTrace();
50             }
51             //下面用子線程的Handler發送消息
52             thread.handler.sendEmptyMessage(1);
53             //下面是主線程發送消息
54             mHandler.sendEmptyMessage(1);
55     }
56 
57 }

      在程序的MyThread類中,我們首先創建自己的Looper,然后使用Handler的無參數構造器(無參構造器默認的是與當前線程相關聯)。最后千萬要記得調用Looper.loop方法,這樣子才能讓消息隊列循環輪詢(即是個死循環)。然后在onCreate方法中讓主線程休眠2秒后(主要是為了便於觀看結果),再讓子線程的handler發送消息,當然我了區別的觀察結果,我們也建立了一個主線程的Hander,即mHandler,也讓它發送了消息。打印的結果如下:

 

       通過打印結果,我們發現打印出來的當前線程ID是不一樣的,充分說明handler是與創建的子線程相關聯的。總結一下吧。

       實現與子線程關聯的Handler,必須要:

1. 必須先創建Looper,調用Looper.prepare方法即可
2. 實例化Handler,與之關聯
3. 調用Looper.loop方法,實現循環不斷讀取消息

       上面三步是要放在run方法中編寫的。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM