1. 程式人生 > >android的訊息處理機制——Looper,Handler,Message (原理圖、原始碼)

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

public class LooperThread extends Thread {

@Override

public void run() {

// 將當前執行緒初始化為Looper執行緒

Looper.prepare();

// ...其他處理,如例項化handler

// 開始迴圈處理訊息佇列

Looper.loop();

}

}

通過上面兩行核心程式碼,你的執行緒就升級為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

public class Looper {

// 每個執行緒中的Looper物件其實是一個ThreadLocal,即執行緒本地儲存(TLS)物件

private static final ThreadLocal sThreadLocal = new ThreadLocal();

// Looper內的訊息佇列

final MessageQueue mQueue;

// 當前執行緒

Thread mThread;

//其他屬性

// 每個Looper物件中有它的訊息佇列,和它所屬的執行緒

private Looper() {

mQueue = new MessageQueue();

mRun = true;

mThread = Thread.currentThread();

}

// 我們呼叫該方法會在呼叫執行緒的TLS中建立Looper物件

public static final void prepare() {

if (sThreadLocal.get() != null) {

// 試圖在有Looper的執行緒中再次建立Looper將丟擲異常

throw new RuntimeException("Only one Looper may be created per thread");

}

sThreadLocal.set(new Looper());

}

// 其他方法

}

  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

public static final void loop() {

Looper me = myLooper();  //得到當前執行緒Looper

MessageQueue queue = me.mQueue;  //得到當前looper的MQ

Binder.clearCallingIdentity();

final long ident = Binder.clearCallingIdentity();

// 開始迴圈

while (true) {

Message msg = queue.next(); // 取出message

if (msg != null) {

if (msg.target == null) {

// message沒有target為結束訊號,退出迴圈

return;

}

// 日誌

if (me.mLogging!= null) me.mLogging.println(

">>>>> Dispatching to " + msg.target + " "

+ msg.callback + ": " + msg.what

);

// 非常重要!將真正的處理工作交給message的target,即後面要講的handler

msg.target.dispatchMessage(msg);

// 日誌

if (me.mLogging!= null) me.mLogging.println(

"<<<<< Finished to    " + msg.target + " "

+ msg.callback);

final long newIdent = Binder.clearCallingIdentity();

if (ident != newIdent) {

Log.wtf("Looper""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);

}

// 回收message資源

msg.recycle();

}

}

}

  除了prepare()和loop()方法,Looper類還提供了一些有用的方法,比如Looper.myLooper()得到當前執行緒looper物件

1

2

3

4

public static final Looper myLooper() {

// 在任意執行緒呼叫Looper.myLooper()返回的都是那個執行緒的looper

return (Looper)sThreadLocal.get();

}

  getThread()得到looper物件所屬執行緒:

1

2

3

public Thread getThread() {

return mThread;

}

  quit()方法結束looper迴圈:

1

2

3

4

5

6

public void quit() {

// 建立一個空的message,它的target為NULL,表示結束迴圈訊息

Message msg = Message.obtain();

// 發出訊息

mQueue.enqueueMessage(msg, 0);

}

綜上,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

public class handler {

final MessageQueue mQueue;  // 關聯的MQ

final Looper mLooper;  // 關聯的looper

final Callback mCallback; 

// 其他屬性

public Handler() {

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());

}

}

// 預設將關聯當前執行緒的looper

mLooper = Looper.myLooper();

// looper不能為空,即該預設的構造方法只能在looper執行緒中使用

if (mLooper == null) {

throw new RuntimeException(

"Can't create handler inside thread that has not called Looper.prepare()");

}

// 重要!!!直接把關聯looper的MQ作為自己的MQ,因此它的訊息將傳送到關聯looper的MQ上

mQueue = mLooper.mQueue;

mCallback = null;

}

// 其他方法

}

  下面我們就可以為之前的LooperThread類加入Handler:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

public class LooperThread extends Thread {

private Handler handler1;

private Handler handler2;

@Override

public void run() {

// 將當前執行緒初始化為Looper執行緒

Looper.prepare();

// 例項化兩個handler

handler1 = new Handler();

handler2 = new Handler();

// 開始迴圈處理訊息佇列

Looper.loop();

}

}

加入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

// 此方法用於向關聯的MQ上傳送Runnable物件,它的run方法將在handler關聯的looper執行緒中執行

public final boolean post(Runnable r)

{

// 注意getPostMessage(r)將runnable封裝成message

return  sendMessageDelayed(getPostMessage(r), 0);

}

private final Message getPostMessage(Runnable r) {

Message m = Message.obtain();  //得到空的message

m.callback = r;  //將runnable設為message的callback,

return m;

}

public boolean sendMessageAtTime(Message msg, long uptimeMillis)

{

boolean sent = false;

MessageQueue queue = mQueue;

if (queue != null) {

msg.target = this;  // message的target必須設為該handler!

sent = queue.enqueueMessage(msg, uptimeMillis);

}

else {

RuntimeException e = new RuntimeException(

this " sendMessageAtTime() called with no mQueue");

Log.w("Looper", e.getMessage(), e);

}

return sent;

}

通過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)

完成的,見原始碼 

  1. public void dispatchMessage(Message msg) {  
  2.         if (msg.callback != null) {  
  3.             handleCallback(msg);  
  4.         } else {  
  5.             if (mCallback != null) {  
  6.                 if (mCallback.handleMessage(msg)) {  
  7.                     return;  
  8.                 }  
  9.             }  
  10.             handleMessage(msg);  
  11.         }  
  12.     } 

  可以看到,除了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 程序執行圖:

androidProcess

從圖中我們可以看到,當我們從外部呼叫元件的時候,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來進行執行緒建的通訊,關係如下圖;

     mainthread

下面是Handler,Message,Message Queue 之間的關係圖

Handlers

這個圖有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  在CODE上檢視程式碼片派生到我的程式碼片
  1. public static final void prepare() {  
  2.         if (sThreadLocal.get() != null) {  
  3.             throw new RuntimeException("Only one Looper may be created per thread");  
  4.         }  
  5.         sThreadLocal.set(new Looper(true));  
  6. }  

sThreadLocal是一個ThreadLocal物件,可以在一個執行緒中儲存變數。可以看到,在第5行,將一個Looper的例項放入了ThreadLocal,並且2-4行判斷了sThreadLocal是否為null,否則丟擲異常。這也就說明了Looper.prepare()方法不能被呼叫兩次,同時也保證了一個執行緒中只有一個Looper例項~相信有些哥們一定遇到這個錯誤。 下面看Looper的構造方法:

[java] view plain copy  在CODE上檢視程式碼片派生到我的程式碼片
  1. private Looper(boolean quitAllowed) {  
  2.         mQueue = new MessageQueue(quitAllowed);  
  3.         mRun = true;  
  4.         mThread = Thread.currentThread();  
  5. }  

在構造方法中,建立了一個MessageQueue(訊息佇列)。 然後我們看loop()方法:

[java] view plain copy  在CODE上檢視程式碼片派生到我的程式碼片
  1. public static void loop() {  
  2.         final Looper me = myLooper();  
  3.         if (me == null) {  
  4.             throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");  
  5.         }  
  6.         final MessageQueue queue = me.mQueue;  
  7.         // Make sure the identity of this thread is that of the local process,  
  8.         // and keep track of what that identity token actually is.  
  9.         Binder.clearCallingIdentity();  
  10.         final long ident = Binder.clearCallingIdentity();  
  11.         for (;;) {  
  12.             Message msg = queue.next(); // might block  
  13.             if (msg == null) {  
  14.                 // No message indicates that the message queue is quitting.  
  15.                 return;  
  16.             }  
  17.             // This must be in a local variable, in case a UI event sets the logger  
  18.             Printer logging = me.mLogging;  
  19.             if (logging != null) {  
  20.                 logging.println(">>>>> Dispatching to " + msg.target + " " +  
  21.                         msg.callback + ": " + msg.what);  
  22.             }  
  23.             msg.target.dispatchMessage(msg);  
  24.             if (logging != null) {  
  25.                 logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);  
  26.             }  
  27.             // Make sure that during the course of dispatching the  
  28.             // identity of the thread wasn't corrupted.  
  29.             final long newIdent = Binder.clearCallingIdentity();  
  30.             if (ident != newIdent) {  
  31.                 Log.wtf(TAG, "Thread identity changed from 0x"  
  32.                         + Long.toHexString(ident) + " to 0x"  
  33.                         + Long.toHexString(newIdent) + " while dispatching to "  
  34.                         + msg.target.getClass().getName() + " "  
  35.                         + msg.callback + " what=" + msg.what);  
  36.             }  
  37.             msg.recycle();  
  38.         }  
  39. }  

第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  在CODE上檢視程式碼片派生到我的程式碼片
  1. public Handler() {  
  2.         this(null, false);  
  3. }  
  4. public Handler(Callback callback, boolean async) {  
  5.         if (FIND_POTENTIAL_LEAKS) {  
  6.             final Class<? extends Handler> klass = getClass();  
  7.             if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&  
  8.                     (klass.getModifiers() & Modifier.STATIC) == 0) {  
  9.                 Log.w(TAG, "The following Handler class should be static or leaks might occur: " +  
  10.                     klass.getCanonicalName());  
  11.             }  
  12.         }  
  13.         mLooper = Looper.myLooper();  
  14.         if (mLooper == null) {  
  15.             throw new RuntimeException(  
  16.                 "Can't create handler inside thread that has not called Looper.prepare()");  
  17.         }  
  18.         mQueue = mLooper.mQueue;  
  19.         mCallback = callback;  
  20.         mAsynchronous = async;  
  21.     }  

14行:通過Looper.myLooper()獲取了當前執行緒儲存的Looper例項,然後在19行又獲取了這個Looper例項中儲存的MessageQueue(訊息佇列),這樣就保證了handler的例項與我們Looper例項中MessageQueue關聯上了。

然後看我們最常用的sendMessage方法

[java] view plain copy  在CODE上檢視程式碼片派生到我的程式碼片
  1. public final boolean sendMessage(Message msg)  
  2.  {  
  3.      return sendMessageDelayed(msg, 0);  
  4.  }  
[java] view plain copy  在CODE上檢視程式碼片派生到我的程式碼片
  1. public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {  
  2.      Message msg = Message.obtain();  
  3.      msg.what = what;  
  4.      return sendMessageDelayed(msg, delayMillis);  
  5.  }  
[java] view plain copy  在CODE上檢視程式碼片派生到我的程式碼片
  1. public final boolean sendMessageDelayed(Message msg, long delayMillis)  
  2.    {  
  3.        if (delayMillis < 0) {  
  4.            delayMillis = 0;  
  5.        }  
  6.        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);  
  7.    }  
[java] view plain copy  在CODE上檢視程式碼片派生到我的程式碼片
  1. public boolean sendMessageAtTime(Message msg, long uptimeMillis) {  
  2.        MessageQueue queue = mQueue;  
  3.        if (queue == null) {  
  4.            RuntimeException e = new RuntimeException(  
  5.                    this + " sendMessageAtTime() called with no mQueue");  
  6.            Log.w("Looper", e.getMessage(), e);  
  7.            return false;  
  8.        }  
  9.        return enqueueMessage(queue, msg, uptimeMillis);  
  10.    }  

輾轉反則最後呼叫了sendMessageAtTime,在此方法內部有直接獲取MessageQueue然後呼叫了enqueueMessage方法,我們再來看看此方法:

[java] view plain copy  在CODE上檢視程式碼片派生到我的程式碼片
  1. private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {  
  2.        msg.target = this;  
  3.        if (mAsynchronous) {  
  4.            msg.setAsynchronous(true);  
  5.        }  
  6.        return queue.enqueueMessage(msg, uptimeMillis);  
  7.    }  

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  在CODE上檢視程式碼片派生到我的程式碼片
  1. public void dispatchMessage(Message msg) {  
  2.         if (msg.callback != null) {  
  3.             handleCallback(msg);  
  4.         } else {  
  5.             if (mCallback != null) {  
  6.                 if (mCallback.handleMessage(msg)) {  
  7.                     return;  
  8.                 }  
  9.             }  
  10.             handleMessage(msg);  
  11.         }  
  12.     }  

可以看到,第10行,呼叫了handleMessage方法,下面我們去看這個方法:

[java] view plain copy  在CODE上檢視程式碼片派生到我的程式碼片
  1. /** 
  2.    * Subclasses must implement this to receive messages. 
  3.    */  
  4.   public void handleMessage(Message msg) {  
  5.   }  

可以看到這是一個空方法,為什麼呢,因為訊息的最終回撥是由我們控制的,我們在建立handler的時候都是複寫handleMessage方法,然後根據msg.what進行訊息處理。

例如:

[java] view plain copy  在CODE上檢視程式碼片派生到我的程式碼片
  1. private Handler mHandler = new Handler()  
  2.     {  
  3.         public void handleMessage(android.os.Message msg)  
  4.         {  
  5.             switch (msg.what)  
  6.             {  
  7.             case value:  
  8.                 break;  
  9.             default:  
  10.                 break;  
  11.             }  
  12.         };  
  13.     };  

到此,這個流程已經解釋完畢,讓我們首先總結一下

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  在CODE上檢視程式碼片派生到我的程式碼片
  1. mHandler.post(new Runnable()  
  2.         {  
  3.             @Override  
  4.             public void run()  
  5.             {  
  6.                 Log.e("TAG", Thread.currentThread().getName());  
  7.                 mTxt.setText("yoxi");  
  8.             }  
  9.         });  

然後run方法中可以寫更新UI的程式碼,其實這個Runnable並沒有建立什麼執行緒,而是傳送了一條訊息,下面看原始碼:

[java] view p