android的訊息處理機制——Looper,Handler,Message (原理圖、原始碼)
轉自:http://my.oschina.net/u/1391648/blog/282892
在開始討論android的訊息處理機制前,先來談談一些基本相關的術語。
通訊的同步(Synchronous):指向客戶端傳送請求後,必須要在服務端有迴應後客戶端才繼續傳送其它的請求,所以這時所有請求將會在服務端得到同步,直到服務端返回請求。
通訊的非同步(Asynchronous):指客戶端在傳送請求後,不必等待服務端的迴應就可以傳送下一個請求。
所謂同步呼叫,就是在一個函式或方法呼叫時,沒有得到結果之前,該呼叫就不返回,直到返回結果。非同步呼叫和同步是相對的,在一個非同步呼叫發起後,被呼叫者立即返回給呼叫者
android的訊息處理有三個核心類:Looper,Handler和Message。其實還有一Message Queue(訊息佇列),但是MQ被封裝到Looper裡面了,我們不會直接與MQ打交道,所以它不算是個核心類。
1. 訊息類:Message類
android.os.Message的主要功能是進行訊息的封裝,同時可以指定訊息的操作形式,Message類定義的變數和常用方法如下:
(1)public int what:變數,用於定義此Message屬於何種操作
(2)public Object obj:變數,用於定義此Message傳遞的資訊資料,通過它傳遞資訊
(3)public int arg1:變數,傳遞一些整型資料時使用
(4)public int arg2:變數,傳遞一些整型資料時使用
(5)public Handler getTarget():普通方法,取得操作此訊息的Handler物件。
在整個訊息處理機制中,message又叫task,封裝了任務攜帶的資訊和處理該任務的handler。message的用法比較簡單,但是有這麼幾點需要注意:
(1)儘管Message有public的預設構造方法,但是你應該通過Message.obtain()來從訊息池中獲得空訊息物件,以節省資源。
(2)如果你的message只需要攜帶簡單的int資訊,請優先使用Message.arg1和Message.arg2來傳遞資訊,這比用Bundle更省記憶體
(3)擅用message.what來標識資訊,以便用不同方式處理message。
(4)使用setData()存放Bundle物件。???
2. 訊息通道:Looper
在使用Handler處理Message時,需要Looper(通道)來完成。在一個Activity中,系統會自動幫使用者啟動Looper物件,而在一個使用者自定義的類中,則需要使用者手工呼叫Looper類中的方法,然後才可以正常啟動Looper物件。Looper的字面意思是“迴圈者”,它被設計用來使一個普通執行緒變成Looper執行緒。所謂Looper執行緒就是迴圈工作的執行緒。在程式開發中(尤其是GUI開發中),我們經常會需要一個執行緒不斷迴圈,一旦有新任務則執行,執行完繼續等待下一個任務,這就是Looper執行緒。使用Looper類建立Looper執行緒很簡單:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
通過上面兩行核心程式碼,你的執行緒就升級為Looper執行緒了!那麼這兩行程式碼都做了些什麼呢?
1)Looper.prepare():建立Looper物件。
通過上圖可以看到,現在你的執行緒中有一個Looper物件,它的內部維護了一個訊息佇列MQ。注意,一個Thread只能有一個Looper物件,為什麼呢?來看一下原始碼
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
|
prepare()背後的工作方式一目瞭然,其核心就是將looper物件定義為ThreadLocal。
2)Looper.loop():迴圈獲取MQ中的訊息,併發送給相應Handler物件。
呼叫loop方法後,Looper執行緒就開始真正工作了,它不斷從自己的MQ中取出隊頭的訊息(也叫任務)執行。其原始碼分析如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
|
除了prepare()和loop()方法,Looper類還提供了一些有用的方法,比如Looper.myLooper()得到當前執行緒looper物件:
1 2 3 4 |
|
getThread()得到looper物件所屬執行緒:
1 2 3 |
|
quit()方法結束looper迴圈:
1 2 3 4 5 6 |
|
綜上,Looper有以下幾個要點:
1)每個執行緒有且只能有一個Looper物件,它是一個ThreadLocal
2)Looper內部有一個訊息佇列,loop()方法呼叫後執行緒開始不斷從佇列中取出訊息執行
3)Looper使一個執行緒變成Looper執行緒。
那麼,我們如何操作Message Queue上的訊息呢?這就是Handler的用處了
3. 訊息操作類:Handler類
Message物件封裝了所有的訊息,而這些訊息的操作需要android.os.Handler類完成。什麼是handler?handler起到了處理MQ上的訊息的作用(只處理由自己發出的訊息),即通知MQ它要執行一個任務(sendMessage),並在loop到自己的時候執行該任務(handleMessage),整個過程是非同步的。handler建立時會關聯一個looper,預設的構造方法將關聯當前執行緒的looper,不過這也是可以set的。預設的構造方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
|
下面我們就可以為之前的LooperThread類加入Handler:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
加入handler後的效果如下圖:
可以看到,一個執行緒可以有多個Handler,但是隻能有一個Looper!
Handler傳送訊息
有了handler之後,我們就可以使用
post(Runnable)
postAtTime(Runnable, long)
postDelayed(Runnable, long)
sendEmptyMessage(int)
sendMessage(Message)
sendMessageAtTime(Message, long)
sendMessageDelayed(Message, long)
這些方法向MQ上傳送訊息了。光看這些API你可能會覺得handler能發兩種訊息,一種是Runnable物件,一種是message物件,這是直觀的理解,但其實post發出的Runnable物件最後都被封裝成message物件了,見原始碼:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
|
通過handler發出的message有如下特點:
1.message.target為該handler物件,這確保了looper執行到該message時能找到處理它的handler,即loop()方法中的關鍵程式碼
msg.target.dispatchMessage(msg);
2.post發出的message,其callback為Runnable物件
Handler處理訊息
說完了訊息的傳送,再來看下handler如何處理訊息。訊息的處理是通過核心方法dispatchMessage(Message msg)與鉤子方法handleMessage(Message msg)
完成的,見原始碼
- public void dispatchMessage(Message msg) {
- if (msg.callback != null) {
- handleCallback(msg);
- } else {
- if (mCallback != null) {
- if (mCallback.handleMessage(msg)) {
- return;
- }
- }
- handleMessage(msg);
- }
- }
可以看到,除了handleMessage(Message msg)和Runnable物件的run方法由開發者實現外(實現具體邏輯),handler的內部工作機制對開發者是透明的。Handler擁有下面兩個重要的特點:
1)handler可以在任意執行緒傳送訊息,這些訊息會被新增到關聯的MQ上
2)訊息的處理是通過核心方法dispatchMessage(Message msg)與鉤子方法handleMessage(Message msg)完成的,handler是在它關聯的looper執行緒中處理訊息的。
這就解決了android最經典的不能在其他非主執行緒中更新UI的問題。android的主執行緒也是一個looper執行緒(looper在android中運用很廣),我們在其中建立的handler預設將關聯主執行緒MQ。因此,利用handler的一個solution就是在activity中建立handler並將其引用傳遞給worker thread,worker thread執行完任務後使用handler傳送訊息通知activity更新UI。(過程如圖)
下面給出sample程式碼,僅供參考:
TestDriverActivity Activity { TextView textview; @Override onCreate(Bundle savedInstanceState) { .onCreate(savedInstanceState); setContentView(R.layout.main); textview = (TextView) findViewById(R.id.textview); Thread workerThread = Thread( SampleTask( MyHandler())); workerThread.start(); } appendText(String msg) { textview.setText(textview.getText() + "\n" + msg); } MyHandler Handler { @Override handleMessage(Message msg) { String result = msg.getData().getString("message"); appendText(result); } } }
SampleTask Runnable { String TAG = SampleTask..getSimpleName(); Handler handler; SampleTask(Handler handler) { (); .handler = handler; } @Override run() { { Thread.sleep(5000); Message msg = prepareMessage("task completed!"); handler.sendMessage(msg); } (InterruptedException e) { Log.d(TAG, "interrupted!"); } } Message prepareMessage(String str) { Message result = handler.obtainMessage(); Bundle data = Bundle(); data.putString("message", str); result.setData(data); result; } }
轉自:http://www.cnblogs.com/youxilua/archive/2011/11/25/2263825.html
前言: 很早以前,學習android的時候就接觸過Handler ,知道Handler是一個用於執行緒間通訊的類,最常用於做下載條,最近,看了Pro android 3 這本書,裡面描述的Handler 說得非常的細緻,與此,寫下Handler的學習筆記
Android 執行的程序
為了,更好的瞭解Handler的機制,我們應該首先,將Android系統整個執行程序都要爛熟於心,下面是android 程序執行圖:
從圖中我們可以看到,當我們從外部呼叫元件的時候,Service 和 ContentProvider 是從執行緒池那裡獲取執行緒,而Activity 和BroadcastReceiver是直接在主執行緒執行,為了,追蹤執行緒,我們可以用debug 方法,或者使用一個工具類,這裡,我們建立一個用於監視執行緒的工具類
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
/**
* @author Tom_achai
* @
date
2011-11-20
*
*/
public class Utils {
public static long getThreadId(){
Thread t = Thread.currentThread();
return
t.getId();
}
/**
* 獲取單獨執行緒資訊
* @
return
*/
public static String getThreadSignature(){
Thread t = Thread.currentThread();
long l = t.getId();
String name = t.getName();
long p = t.getPriority();
String gname = t.getThreadGroup().getName();
return
(
"(Thread):"
+name+
":(id)"
+ l +
"(:priority)"
+ p +
":(group)"
+ gname );
}
/**
*獲取當前執行緒 資訊
*/
public static void logThreadSignature(){
Log.d(
"ThreadUtils"
, getThreadSignature());
}
public static void logThreadSignature(String name ){
Log.d(
"ThreadUtils"
, name +
":"
+getThreadSignature());
}
public static void sleepForInSecs(int secs){
try{
Thread.
sleep
(secs * 1000);
}catch (Exception e) {
//
TODO: handle exception
e.printStackTrace();
}
}
/**
* 講String放進Bundle 中
* @param message
* @
return
*/
public static Bundle getStringAsBundle(String message){
Bundle b = new Bundle();
b.putString(
"message"
, message);
return
b;
}
/**
*
* 獲取Bundle的String
* @param b
* @
return
*/
public static String getStringFromABundle(Bundle b){
return
b.getString(
"message"
);
}
}
有了這樣一個類就可以方便我們觀察執行緒的執行
好了,現在準備好以後就進入正題Handler
Handlers
為什麼要使用Handlers?
因為,我們當我們的主執行緒佇列,如果處理一個訊息超過5秒,android 就會丟擲一個 ANP(無響應)的訊息,所以,我們需要把一些要處理比較長的訊息,放在一個單獨執行緒裡面處理,把處理以後的結果,返回給主執行緒執行,就需要用的Handler來進行執行緒建的通訊,關係如下圖;
下面是Handler,Message,Message Queue 之間的關係圖
這個圖有4個地方關係到handlers
1, 主執行緒(Main thread)
2, 主執行緒佇列(Main thread queue)
3,Hanlder
4,Message
上面的四個地方,主執行緒,和主執行緒的佇列我們無需處理,所以,我們主要是處理Handler 和 Message 之間的關係.
我們每發出一個Message,Message就會落在主執行緒的隊列當中,然後,Handler就可以呼叫Message繫結的資料,對主執行緒的元件進行操作.
Message
作為handler接受的物件,我們有必要知道Message這個資料型別是個怎樣的資料型別
從官方文件中我們可以知道message 關於資料的欄位
public int what public int arg1 public int arg2 public Object obj 從上面的表格可以看出,message 提供了一個物件來儲存物件,而且,還提供了三個int欄位用來儲存少量int型別
當然,除了以上三個Message 自有的欄位外,我們還可以通過setData(Bundle b),來儲存一個Bundle物件,來儲存更豐富的資料型別,例如,圖片等等.
在初始化我們的message的時候就可以為我們的Message預設欄位賦值,注意賦值順序!!!
1
2
3
4
5
6
7
8
9
Message msg = obtainMessage();
//
設定我們what 欄位的初值,注意順序!!!
Message msg = mHandler.obtainMessage(int what);
//
下面同理
Message msg = mHandler.obtainMessage(int what,Object object);
Message msg = mHandler.obtainMessage(int what,int arg1,int arg2);
Message msg = mHandler.obtainMessage(int what,int arg1,int arg2, Object obj
);
轉自:http://blog.csdn.net/itachi85/article/details/8035333
andriod提供了Handler 和 Looper 來滿足執行緒間的通訊。Handler先進先出原則。Looper類用來管理特定執行緒內物件之間的訊息交換(MessageExchange)。 1)Looper: 一個執行緒可以產生一個Looper物件,由它來管理此執行緒裡的MessageQueue(訊息佇列)。 2)Handler: 你可以構造Handler物件來與Looper溝通,以便push新訊息到MessageQueue裡;或者接收Looper從Message Queue取出)所送來的訊息。 3) Message Queue(訊息佇列):用來存放執行緒放入的訊息。
4)執行緒:UIthread 通常就是main thread,而Android啟動程式時會替它建立一個MessageQueue。
1.Handler建立訊息
每一個訊息都需要被指定的Handler處理,通過Handler建立訊息便可以完成此功能。Android訊息機制中引入了訊息池。Handler建立訊息時首先查詢訊息池中是否有訊息存在,如果有直接從訊息池中取得,如果沒有則重新初始化一個訊息例項。使用訊息池的好處是:訊息不被使用時,並不作為垃圾回收,而是放入訊息池,可供下次Handler建立訊息時使用。訊息池提高了訊息物件的複用,減少系統垃圾回收的次數。訊息的建立流程如圖所示。
2.Handler傳送訊息
UI主執行緒初始化第一個Handler時會通過ThreadLocal建立一個Looper,該Looper與UI主執行緒一一對應。使用ThreadLocal的目的是保證每一個執行緒只建立唯一一個Looper。之後其他Handler初始化的時候直接獲取第一個Handler建立的Looper。Looper初始化的時候會建立一個訊息佇列MessageQueue。至此,主執行緒、訊息迴圈、訊息佇列之間的關係是1:1:1。
Handler、Looper、MessageQueue的初始化流程如圖所示:
Hander持有對UI主執行緒訊息佇列MessageQueue和訊息迴圈Looper的引用,子執行緒可以通過Handler將訊息傳送到UI執行緒的訊息佇列MessageQueue中。
3.Handler處理訊息
UI主執行緒通過Looper迴圈查詢訊息佇列UI_MQ,當發現有訊息存在時會將訊息從訊息佇列中取出。首先分析訊息,通過訊息的引數判斷該訊息對應的Handler,然後將訊息分發到指定的Handler進行處理。
子執行緒通過Handler、Looper與UI主執行緒通訊的流程如圖所示。
很多人面試肯定都被問到過,請問Android中的Looper , Handler , Message有什麼關係?本篇部落格目的首先為大家從原始碼角度介紹3者關係,然後給出一個容易記憶的結論。
1、 概述
Handler 、 Looper 、Message 這三者都與Android非同步訊息處理執行緒相關的概念。那麼什麼叫非同步訊息處理執行緒呢? 非同步訊息處理執行緒啟動後會進入一個無限的迴圈體之中,每迴圈一次,從其內部的訊息佇列中取出一個訊息,然後回撥相應的訊息處理函式,執行完成一個訊息後則繼續迴圈。若訊息佇列為空,執行緒則會阻塞等待。
說了這一堆,那麼和Handler 、 Looper 、Message有啥關係?其實Looper負責的就是建立一個MessageQueue,然後進入一個無限迴圈體不斷從該MessageQueue中讀取訊息,而訊息的建立者就是一個或多個Handler 。
2、 原始碼解析
1、Looper
對於Looper主要是prepare()和loop()兩個方法。 首先看prepare()方法
[java] view plain copy
- public static final void prepare() {
- if (sThreadLocal.get() != null) {
- throw new RuntimeException("Only one Looper may be created per thread");
- }
- sThreadLocal.set(new Looper(true));
- }
sThreadLocal是一個ThreadLocal物件,可以在一個執行緒中儲存變數。可以看到,在第5行,將一個Looper的例項放入了ThreadLocal,並且2-4行判斷了sThreadLocal是否為null,否則丟擲異常。這也就說明了Looper.prepare()方法不能被呼叫兩次,同時也保證了一個執行緒中只有一個Looper例項~相信有些哥們一定遇到這個錯誤。 下面看Looper的構造方法:
[java] view plain copy
- private Looper(boolean quitAllowed) {
- mQueue = new MessageQueue(quitAllowed);
- mRun = true;
- mThread = Thread.currentThread();
- }
在構造方法中,建立了一個MessageQueue(訊息佇列)。 然後我們看loop()方法:
[java] view plain copy
- public static void loop() {
- final Looper me = myLooper();
- if (me == null) {
- throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
- }
- final MessageQueue queue = me.mQueue;
- // Make sure the identity of this thread is that of the local process,
- // and keep track of what that identity token actually is.
- Binder.clearCallingIdentity();
- final long ident = Binder.clearCallingIdentity();
- for (;;) {
- Message msg = queue.next(); // might block
- if (msg == null) {
- // No message indicates that the message queue is quitting.
- return;
- }
- // This must be in a local variable, in case a UI event sets the logger
- Printer logging = me.mLogging;
- if (logging != null) {
- logging.println(">>>>> Dispatching to " + msg.target + " " +
- msg.callback + ": " + msg.what);
- }
- msg.target.dispatchMessage(msg);
- if (logging != null) {
- logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
- }
- // Make sure that during the course of dispatching the
- // identity of the thread wasn't corrupted.
- final long newIdent = Binder.clearCallingIdentity();
- if (ident != newIdent) {
- Log.wtf(TAG, "Thread identity changed from 0x"
- + Long.toHexString(ident) + " to 0x"
- + Long.toHexString(newIdent) + " while dispatching to "
- + msg.target.getClass().getName() + " "
- + msg.callback + " what=" + msg.what);
- }
- msg.recycle();
- }
- }
第2行: public static Looper myLooper() { return sThreadLocal.get(); } 方法直接返回了sThreadLocal儲存的Looper例項,如果me為null則丟擲異常,也就是說looper方法必須在prepare方法之後執行。 第6行:拿到該looper例項中的mQueue(訊息佇列) 13到45行:就進入了我們所說的無限迴圈。 14行:取出一條訊息,如果沒有訊息則阻塞。 27行:使用呼叫 msg.target.dispatchMessage(msg);把訊息交給msg的target的dispatchMessage方法去處理。Msg的target是什麼呢?其實就是handler物件,下面會進行分析。 44行:釋放訊息佔據的資源。 Looper主要作用: 1、 與當前執行緒繫結,保證一個執行緒只會有一個Looper例項,同時一個Looper例項也只有一個MessageQueue。 2、 loop()方法,不斷從MessageQueue中去取訊息,交給訊息的target屬性的dispatchMessage去處理。 好了,我們的非同步訊息處理執行緒已經有了訊息佇列(MessageQueue),也有了在無限迴圈體中取出訊息的哥們,現在缺的就是傳送訊息的物件了,於是乎:Handler登場了。
2、Handler
使用Handler之前,我們都是初始化一個例項,比如用於更新UI執行緒,我們會在宣告的時候直接初始化,或者在onCreate中初始化Handler例項。所以我們首先看Handler的構造方法,看其如何與MessageQueue聯絡上的,它在子執行緒中傳送的訊息(一般傳送訊息都在非UI執行緒)怎麼傳送到MessageQueue中的。
[java] view plain copy
- public Handler() {
- this(null, false);
- }
- public Handler(Callback callback, boolean async) {
- if (FIND_POTENTIAL_LEAKS) {
- final Class<? extends Handler> klass = getClass();
- if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
- (klass.getModifiers() & Modifier.STATIC) == 0) {
- Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
- klass.getCanonicalName());
- }
- }
- mLooper = Looper.myLooper();
- if (mLooper == null) {
- throw new RuntimeException(
- "Can't create handler inside thread that has not called Looper.prepare()");
- }
- mQueue = mLooper.mQueue;
- mCallback = callback;
- mAsynchronous = async;
- }
14行:通過Looper.myLooper()獲取了當前執行緒儲存的Looper例項,然後在19行又獲取了這個Looper例項中儲存的MessageQueue(訊息佇列),這樣就保證了handler的例項與我們Looper例項中MessageQueue關聯上了。
然後看我們最常用的sendMessage方法
[java] view plain copy[java] view plain copy
- public final boolean sendMessage(Message msg)
- {
- return sendMessageDelayed(msg, 0);
- }
[java] view plain copy
- public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
- Message msg = Message.obtain();
- msg.what = what;
- return sendMessageDelayed(msg, delayMillis);
- }
[java] view plain copy
- public final boolean sendMessageDelayed(Message msg, long delayMillis)
- {
- if (delayMillis < 0) {
- delayMillis = 0;
- }
- return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
- }
- public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
- MessageQueue queue = mQueue;
- if (queue == null) {
- RuntimeException e = new RuntimeException(
- this + " sendMessageAtTime() called with no mQueue");
- Log.w("Looper", e.getMessage(), e);
- return false;
- }
- return enqueueMessage(queue, msg, uptimeMillis);
- }
輾轉反則最後呼叫了sendMessageAtTime,在此方法內部有直接獲取MessageQueue然後呼叫了enqueueMessage方法,我們再來看看此方法:
[java] view plain copy
- private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
- msg.target = this;
- if (mAsynchronous) {
- msg.setAsynchronous(true);
- }
- return queue.enqueueMessage(msg, uptimeMillis);
- }
enqueueMessage中首先為meg.target賦值為this,【如果大家還記得Looper的loop方法會取出每個msg然後交給msg,target.dispatchMessage(msg)去處理訊息】,也就是把當前的handler作為msg的target屬性。最終會呼叫queue的enqueueMessage的方法,也就是說handler發出的訊息,最終會儲存到訊息佇列中去。
現在已經很清楚了Looper會呼叫prepare()和loop()方法,在當前執行的執行緒中儲存一個Looper例項,這個例項會儲存一個MessageQueue物件,然後當前執行緒進入一個無限迴圈中去,不斷從MessageQueue中讀取Handler發來的訊息。然後再回調建立這個訊息的handler中的dispathMessage方法,下面我們趕快去看一看這個方法:
[java] view plain copy
- public void dispatchMessage(Message msg) {
- if (msg.callback != null) {
- handleCallback(msg);
- } else {
- if (mCallback != null) {
- if (mCallback.handleMessage(msg)) {
- return;
- }
- }
- handleMessage(msg);
- }
- }
可以看到,第10行,呼叫了handleMessage方法,下面我們去看這個方法:
[java] view plain copy
- /**
- * Subclasses must implement this to receive messages.
- */
- public void handleMessage(Message msg) {
- }
可以看到這是一個空方法,為什麼呢,因為訊息的最終回撥是由我們控制的,我們在建立handler的時候都是複寫handleMessage方法,然後根據msg.what進行訊息處理。
例如:
[java] view plain copy
- private Handler mHandler = new Handler()
- {
- public void handleMessage(android.os.Message msg)
- {
- switch (msg.what)
- {
- case value:
- break;
- default:
- break;
- }
- };
- };
到此,這個流程已經解釋完畢,讓我們首先總結一下
1、首先Looper.prepare()在本執行緒中儲存一個Looper例項,然後該例項中儲存一個MessageQueue物件;因為Looper.prepare()在一個執行緒中只能呼叫一次,所以MessageQueue在一個執行緒中只會存在一個。
2、Looper.loop()會讓當前執行緒進入一個無限迴圈,不端從MessageQueue的例項中讀取訊息,然後回撥msg.target.dispatchMessage(msg)方法。
3、Handler的構造方法,會首先得到當前執行緒中儲存的Looper例項,進而與Looper例項中的MessageQueue想關聯。
4、Handler的sendMessage方法,會給msg的target賦值為handler自身,然後加入MessageQueue中。
5、在構造Handler例項時,我們會重寫handleMessage方法,也就是msg.target.dispatchMessage(msg)最終呼叫的方法。
好了,總結完成,大家可能還會問,那麼在Activity中,我們並沒有顯示的呼叫Looper.prepare()和Looper.loop()方法,為啥Handler可以成功建立呢,這是因為在Activity的啟動程式碼中,已經在當前UI執行緒呼叫了Looper.prepare()和Looper.loop()方法。
3、Handler post
今天有人問我,你說Handler的post方法建立的執行緒和UI執行緒有什麼關係?
其實這個問題也是出現這篇部落格的原因之一;這裡需要說明,有時候為了方便,我們會直接寫如下程式碼:
[java] view plain copy
- mHandler.post(new Runnable()
- {
- @Override
- public void run()
- {
- Log.e("TAG", Thread.currentThread().getName());
- mTxt.setText("yoxi");
- }
- });
然後run方法中可以寫更新UI的程式碼,其實這個Runnable並沒有建立什麼執行緒,而是傳送了一條訊息,下面看原始碼:
[java] view p