總結一下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方法中編寫的。