Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android 異步消息處理機制(Handler 、 Looper 、MessageQueue)源碼解析

Android 異步消息處理機制(Handler 、 Looper 、MessageQueue)源碼解析

編輯:關於Android編程

1、Handler的由來

當程序第一次啟動的時候,Android會同時啟動一條主線程( Main Thread)來負責處理與UI相關的事件,我們叫做UI線程。

Android的UI操作並不是線程安全的(出於性能優化考慮),意味著如果多個線程並發操作UI線程,可能導致線程安全問題。

為了解決Android應用多線程問題—Android平台只允許UI線程修改Activity裡的UI組建,就會導致新啟動的線程無法改變界面組建的屬性值。

簡單的說:當主線程隊列處理一個消息超過5秒,android 就會拋出一個 ANP(無響應)的異常,所以,我們需要把一些要處理比較長的消息,放在一個單獨線程裡面處理,把處理以後的結果,返回給主線程運行,就需要用的Handler來進行線程建的通信。

2、Handler的作用

2.1 讓線程延時執行

主要用到的兩個方法:

final boolean postAtTime(Runnable r, long uptimeMillis)

final boolean postDelayed(Runnable r, long delayMillis)

2.2 讓任務在其他線程中執行並返回結果

分為兩個步驟:

在新啟動的線程中發送消息

使用Handler對象的sendMessage()方法或者SendEmptyMessage()方法發送消息。

在主線程中獲取處理消息

重寫Handler類中處理消息的方法(void handleMessage(Message msg)),當新啟動的線程發送消息時,消息發送到與之關聯的MessageQueue。而Hanlder不斷地從MessageQueue中獲取並處理消息。

3、Handler更新UI線程一般使用

首先要進行Handler 申明,復寫handleMessage方法( 放在主線程中)

private Handler handler = new Handler() {

        @Override
        public void handleMessage(Message msg) {
            // TODO 接收消息並且去更新UI線程上的控件內容
            if (msg.what == UPDATE) {
                // 更新界面上的textview
                tv.setText(String.valueOf(msg.obj));
            }
            super.handleMessage(msg);
        }
    };
子線程發送Message給ui線程表示自己任務已經執行完成,主線程可以做相應的操作了。
new Thread() {
            @Override
            public void run() {
                // TODO 子線程中通過handler發送消息給handler接收,由handler去更新TextView的值
                try {
                       //do something

                        Message msg = new Message();
                        msg.what = UPDATE;                  
                        msg.obj = "更新後的值" ;
                        handler.sendMessage(msg);
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }.start();
4、Handler原理分析

4.1  Handler的構造函數

① public Handler()
② public Handler(Callbackcallback)
③ public Handler(Looperlooper)
④ public Handler(Looperlooper, Callbackcallback)  

第①個和第②個構造函數都沒有傳遞Looper,這兩個構造函數都將通過調用Looper.myLooper()獲取當前線程綁定的Looper對象,然後將該Looper對象保存到名為mLooper的成員字段中。  
  下面來看①②個函數源碼:
  
113    public Handler() {
114        this(null, false);
115    }

127    public Handler(Callback callback) {
128        this(callback, false);
129    }

//他們會調用Handler的內部構造方法

188    public Handler(Callback callback, boolean async) {
189        if (FIND_POTENTIAL_LEAKS) {
190      final Class klass = getClass();
191      if ((klass.isAnonymousClass() ||klass.isMemberClass()
         || klass.isLocalClass()) &&
192                    (klass.getModifiers() & Modifier.STATIC) == 0) {
193                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
194                    klass.getCanonicalName());
195            }
196        }
197/************************************
198        mLooper = Looper.myLooper();
199        if (mLooper == null) {
200            throw new RuntimeException(
201                "Can't create handler inside thread that has not called Looper.prepare()");
202        }
203        mQueue = mLooper.mQueue;
204        mCallback = callback;
205        mAsynchronous = async;
206    }

我們看到暗紅色的重點部分:

通過Looper.myLooper()獲取了當前線程保存的Looper實例,又通過這個Looper實例獲取了其中保存的MessageQueue(消息隊列)。每個Handler 對應一個Looper對象,產生一個MessageQueue
  

第③個和第④個構造函數傳遞了Looper對象,這兩個構造函數會將該Looper保存到名為mLooper的成員字段中。
下面來看③④個函數源碼:
136    public Handler(Looper looper) {
137        this(looper, null, false);
138    } 

147    public Handler(Looper looper, Callback callback) {
148        this(looper, callback, false);
149    }
//他們會調用Handler的內部構造方法

227    public Handler(Looper looper, Callback callback, boolean async) {
228        mLooper = looper;
229        mQueue = looper.mQueue;
230        mCallback = callback;
231        mAsynchronous = async;
232    }
第②個和第④個構造函數還傳遞了Callback對象,Callback是Handler中的內部接口,需要實現其內部的handleMessage方法,Callback代碼如下:
80     public interface Callback {
81         public boolean More ...handleMessage(Message msg);
82     }

  Handler.Callback是用來處理Message的一種手段,如果沒有傳遞該參數,那麼就應該重寫Handler的handleMessage方法,也就是說為了使得Handler能夠處理Message,我們有兩種辦法:
  
1. 向Hanlder的構造函數傳入一個Handler.Callback對象,並實現Handler.Callback的handleMessage方法  
  
2. 無需向Hanlder的構造函數傳入Handler.Callback對象,但是需要重寫Handler本身的handleMessage方法  
   
也就是說無論哪種方式,我們都得通過某種方式實現handleMessage方法,這點與Java中對Thread的設計有異曲同工之處。

4.2 Handle發送消息的幾個方法源碼

   public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }
   public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageDelayed(msg, delayMillis);
    }
 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(),然後返回了enqueueMessage方法,下面看一下此方法源碼:

626    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
      //把當前的handler作為msg的target屬性
627        msg.target = this;
628        if (mAsynchronous) {
629            msg.setAsynchronous(true);
630        }
631        return queue.enqueueMessage(msg, uptimeMillis);
632    }

在該方法中有兩件事需要注意:

msg.target = this

該代碼將Message的target綁定為當前的Handler

queue.enqueueMessage
  
變量queue表示的是Handler所綁定的消息隊列MessageQueue,通過調用queue.enqueueMessage(msg, uptimeMillis)我們將Message放入到消息隊列中。

過下圖可以看到完整的方法調用順序:

這裡寫圖片描述 <喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxzdHJvbmc+NaGiTG9vcGVy1K3A7bfWzvY8L3N0cm9uZz4NCjxwPs7Sw8fSu7Dj1NrW98/fs8zJ6sP3SGFuZGxlcqOs09DKsc7Sw8fQ6NKqvMyz0FRocmVhZMDgyrXP1tfUvLq1xM/fs8y5psTco6y1sc7Sw8fU2sDvw+bJ6sP3SGFuZGxlcrXEyrG68rvhsai07aGjxuTUrdLyysfW98/fs8zW0NLRvq3Ktc/WwcvBvbj21tjSqrXETG9vcGVyt723qKOsz8LD5r+00ru/tEFjdGl2aXR5VGhyZWFkLmphdmHW0G1haW63vbeotcTUtMLro7o8L3A+DQo8cHJlIGNsYXNzPQ=="brush:java;"> public static void main(String[] args) { //......省略 5205 Looper.prepareMainLooper();//> 5206 5207 ActivityThread thread = new ActivityThread(); 5208 thread.attach(false); 5209 5210 if (sMainThreadHandler == null) { 5211 sMainThreadHandler = thread.getHandler(); 5212 } 5213 5214 AsyncTask.init(); 5215 5216 if (false) { 5217 Looper.myLooper().setMessageLogging(new 5218 LogPrinter(Log.DEBUG, "ActivityThread")); 5219 } 5220 5221 Looper.loop();//> 5222 5223 throw new RuntimeException("Main thread loop unexpectedly exited"); 5224 } 5225}

5.1 首先看prepare()方法

70     public static void prepare() {
71         prepare(true);
72     }
73 
74     private static void prepare(boolean quitAllowed) {
     //證了一個線程中只有一個Looper實例
75         if (sThreadLocal.get() != null) {
76             throw new RuntimeException("Only one Looper may be created per thread");
77         }
78         sThreadLocal.set(new Looper(quitAllowed));
79     }

該方法會調用Looper構造函數同時實例化出MessageQueue和當前thread.

186    private Looper(boolean quitAllowed) {
187        mQueue = new MessageQueue(quitAllowed);
188        mThread = Thread.currentThread();
189    } 

182    public static MessageQueue myQueue() {
183        return myLooper().mQueue;
184    }

  prepare()方法中通過ThreadLocal對象實現Looper實例與線程的綁定。

5.2  loop()方法

109    public static void loop() {
110        final Looper me = myLooper();
111        if (me == null) {
112            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
113        }
114        final MessageQueue queue = me.mQueue;
115
118        Binder.clearCallingIdentity();
119        final long ident = Binder.clearCallingIdentity();
120
121        for (;;) {
122            Message msg = queue.next(); // might block
123            if (msg == null) {
124               
125                return;
126            }
127
129            Printer logging = me.mLogging;
130            if (logging != null) {
131                logging.println(">>>>> Dispatching to " + msg.target + " " +
132                        msg.callback + ": " + msg.what);
133            }
//重點****
135            msg.target.dispatchMessage(msg);
136
137            if (logging != null) {
138                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
139            }
140
142            // identity of the thread wasn't corrupted.
143            final long newIdent = Binder.clearCallingIdentity();
144            if (ident != newIdent) {
145                Log.wtf(TAG, "Thread identity changed from 0x"
146                        + Long.toHexString(ident) + " to 0x"
147                        + Long.toHexString(newIdent) + " while dispatching to "
148                        + msg.target.getClass().getName() + " "
149                        + msg.callback + " what=" + msg.what);
150            }
151
152            msg.recycleUnchecked();
153        }
154    }

首先looper對象不能為空,就是說loop()方法調用必須在prepare()方法的後面。

 Looper一直在不斷的從消息隊列中通過MessageQueue的next方法獲取Message,然後通過代碼msg.target.dispatchMessage(msg)讓該msg所綁定的Handler(Message.target)執行dispatchMessage方法以實現對Message的處理。

Handler的dispatchMessage的源碼如下:

93     public void dispatchMessage(Message msg) {
94         if (msg.callback != null) {
95             handleCallback(msg);
96         } else {
97             if (mCallback != null) {
98                 if (mCallback.handleMessage(msg)) {
99                     return;
100                }
101            }
102            handleMessage(msg);
103        }
104    }

我們可以看到Handler提供了三種途徑處理Message,而且處理有前後優先級之分:首先嘗試讓postXXX中傳遞的Runnable執行,其次嘗試讓Handler構造函數中傳入的Callback的handleMessage方法處理,最後才是讓Handler自身的handleMessage方法處理Message。

6、如何在子線程中使用Handler

Handler本質是從當前的線程中獲取到Looper來監聽和操作MessageQueue,當其他線程執行完成後回調當前線程。

子線程需要先prepare()才能獲取到Looper的,是因為在子線程只是一個普通的線程,其ThreadLoacl中沒有設置過Looper,所以會拋出異常,而在Looper的prepare()方法中sThreadLocal.set(new Looper())是設置了Looper的。

6.1 實例代碼

 定義一個類實現Runnable接口或繼承Thread類(一般不繼承)。

    class Rub implements Runnable {  

        public Handler myHandler;  
        // 實現Runnable接口的線程體 
        @Override  
        public void run() {  

         /*①、調用Looper的prepare()方法為當前線程創建Looper對象並,
          創建Looper對象時,它的構造器會自動的創建相對應的MessageQueue*/
            Looper.prepare();  

            /*.②、創建Handler子類的實例,重寫HandleMessage()方法,該方法處理除當前線程以外線程的消息*/
             myHandler = new Handler() {  
                @Override  
                public void handleMessage(Message msg) {  
                    String ms = "";  
                    if (msg.what == 0x777) {  

                    }  
                }  

            };  
            //③、調用Looper的loop()方法來啟動Looper讓消息隊列轉動起來
            Looper.loop();  
        }
    }

注意分成三步: 

1.調用Looper的prepare()方法為當前線程創建Looper對象,創建Looper對象時,它的構造器會創建與之配套的MessageQueue。  

2.有了Looper之後,創建Handler子類實例,重寫HanderMessage()方法,該方法負責處理來自於其他線程的消息。  

3.調用Looper的looper()方法啟動Looper。

然後使用這個handler實例在任何其他線程中發送消息,最終處理消息的代碼都會在你創建Handler實例的線程中運行。

7、總結

Handler
      發送消息,它能把消息發送給Looper管理的MessageQueue。
      處理消息,並負責處理Looper分給它的消息。
Message
      Handler接收和處理的消息對象。
Looper
      每個線程只有一個Looper,它負責管理對應的MessageQueue,會不斷地從MessageQueue取出消息,並將消息分給對應的Hanlder處理。  
      
      主線程中,系統已經初始化了一個Looper對象,因此可以直接創建Handler即可,就可以通過Handler來發送消息、處理消息。 程序自己啟動的子線程,程序必須自己創建一個Looper對象,並啟動它,調用Looper.prepare()方法。

prapare()方法:保證每個線程最多只有一個Looper對象。  

looper()方法:啟動Looper,使用一個死循環不斷取出MessageQueue中的消息,並將取出的消息分給對應的Handler進行處理。  

MessageQueue:由Looper負責管理,它采用先進先出的方式來管理Message。 

Handler的構造方法,會首先得到當前線程中保存的Looper實例,進而與Looper實例中的MessageQueue想關聯。 
  
Handler的sendMessage方法,會給msg的target賦值為handler自身,然後加入MessageQueue中。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved