編輯:關於Android編程
怎樣理解Android中的Handler,MessageQueue、Runnable與Looper?
簡單來講,用一句話概括就是:
Looper不斷獲取MessageQueue中的一個Message(Runnable會包裝成Message),然後由Handler來處理。
這其實就是進程/線程跑起來的基礎,我們稱為消息循環處理機制。其實任何系統能夠跑起來的本質就是依賴於這樣一個機制。
所以我們第一個要搞清楚的問題是在Android中消息循環處理機制是怎麼搭建起來的?
線程是CPU調度的基本單位,也就是說線程相當於一個平台,我們利用這個平台做各種各樣的事情,顯然消息循環機制(Looper)也是在線程上完成的。所以我們首先看下在線程裡是怎麼使用Looper的?舉個簡單的例子:
class LooperThread extends Thread{ @Override public void run() { Looper.prepare(); Handler h = new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); } }; Looper.loop(); } }
從上面的代碼可以看出,Looper的使用是非常簡單的,就3個步驟。
第一步:Looper.prepare();
准備工作,看下源碼裡面做了什麼事情?
public static void prepare() { if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper()); } private Looper() { mQueue = new MessageQueue(); mRun = true; mThread = Thread.currentThread(); }
首先看下sThreadLocal對象是什麼東西?
static final ThreadLocalsThreadLocal = new ThreadLocal ();
哦,原來是ThreadLocal對象,這裡ThreadLocal的意義在於為每個線程隔離Looper對象,也就是每個線程對應一個Looper對象,sThreadLocal就是用來統一管理每個線程的Looper對象,確保每個線程的Looper對象是獨立的。所以Looper.prepare();的作用在於為線程創建一個Looper對象,可以通過sThreadLocal.get()來獲得。從代碼裡可以看出,一個線程對應一個Looper對象。然後在Looper的構造器裡初始化了MessageQueue。
第二步:創建處理消息的Handler;
你肯猜到了,Handler和Looper存在某種關系,不然這個消息循環處理是怎麼連接起來的。所以我們從Handler的構造方法入手。
public Handler() { //省略部分代碼 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 = null; }
從上面的代碼裡我們得到以下幾條信息:
1、在Handler裡通過Looper.myLooper()得到了線程的Looper對象;
public static Looper myLooper() { return sThreadLocal.get(); }
2、在Handler裡維護了一個MessageQueue mQueue,指向Looper對象裡的MessageQueue。
3、有一個回調接口對象 mCallback
好,現在Handler、Looper和MessageQueue算是有了一些聯系。
下面看第三步:Looper.loop();
前面兩步都是准備工作,這一步開始真正做事情了,我們來看下loop()方法裡的主要流程。
public static void loop() { Looper me = myLooper(); if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); } MessageQueue queue = me.mQueue; while (true) { Message msg = queue.next(); // might block if (msg != null) { if (msg.target == null) { return; } msg.target.dispatchMessage(msg); msg.recycle(); } } }
從上面可以看出,消息循環的機制就是從Looper對象中拿到消息隊列MessageQueue,然後不斷的從MessageQueue裡拿到消息,然後由handler(msg.target)去處理(dispatchMessage(msg))。
從創建Looper對象和Handler對象,到handler和Looper之間聯系起來,再到消息循環處理,就這樣,消息循序處理機制搭建起來了,線程跑起來了,整個流程我們似乎也明白了,但其實仔細想想,我們忽略了很多細節:
1、前面我們講了從消息隊列MessageQueue中取出消息message交由handler處理,但好像沒看到過消息是怎麼放進消息隊列的?Message和handler是怎麼聯系起來的也不清楚?
我們知道handler是用來處理消息的,其實handler還有另外一個作用—-發送消息:將Message壓入MessageQueue中。Handler發送消息用到的API分為兩個系列,Post系列和Send系列。
Post系列:
public final boolean post(Runnable r);
public final boolean postAtTime(Runnable r, long uptimeMillis);
…………
Send系列:
public final boolean sendMessage(Message msg);
public final boolean sendEmptyMessage(int what);
public final boolean sendMessageDelayed(Message msg, long delayMillis);
public boolean sendMessageAtTime(Message msg, long uptimeMillis);
……………
Post和Send兩個系列的共同點是它們都負責將某個消息壓入MessageQueue中;區別在於後者處理的函數參數直接是Message,而Post處理的參數是Runnable,將Runnable轉換成Message,再調用Send系列函數來執行下一步。
我們挑選第一個Post函數來分析下其流程:
public final boolean post(Runnable r) { return sendMessageDelayed(getPostMessage(r), 0); }
首先通過方法getPostMessage(r)將Runnable對象封裝成Message ,再通過對應的send函數把它推送到MessageQueue中;
private final Message getPostMessage(Runnable r) { Message m = Message.obtain(); m.callback = r; return m; }
從getPostMessage(r)方法中我們可以知道以下幾點
1)Android系統會維護一個全局的Message池,當用戶需要使用Message時,可以通過obtain直接獲得,而不是自行創建。這樣的設計可以避免不必要的資源浪費。
2)Runnable對象賦給的Message的callback,在Message類中callback的定義如下:
Runnable callback;
public final boolean sendMessageDelayed(Message msg, long delayMillis) { if (delayMillis < 0) { delayMillis = 0; } return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); }
SystemClock.uptimeMillis():從手機開機到現在的毫秒數,不包括睡眠時間。
public boolean sendMessageAtTime(Message msg, long uptimeMillis) { boolean sent = false; MessageQueue queue = mQueue; if (queue != null) { msg.target = this; sent = queue.enqueueMessage(msg, uptimeMillis); } return sent; }
還記得mQueue嗎?在handler的構造器中將Looper裡的MessageQueue賦給了mQueue。
Message和handler也在這裡聯系起來了:msg.target = this;
最後queue.enqueueMessage(msg, uptimeMillis)將消息壓入隊列。
2、對於handler處理消息,上面我們是一筆帶過的,msg.target.dispatchMessage(msg); 那消息處理的流程到底是怎麼樣的呢?
在Handler類中對消息處理有兩個方法:
public void dispatchMessage(Message msg);//對Message進行分發
public void handleMessage(Message msg);//對Message進行處理
Looper從MessageQueue中取出一個Message後,首先會調用Handler. dispatchMessage進行消息派發,
public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }
首先判讀msg.callback是否為空,還記得這個嗎?前面提到過msg.callback就是Post系列傳入的Runnable對象,所以Post系列發送的消息msg.callback不為空,走handleCallback(msg),
private final void handleCallback(Message message) { message.callback.run(); }
直接調用run方法執行任務,消息處理結束。
Send系列發送的消息callback為空,所以走else,先判斷mCallback是否為空,mCallback對象是什麼?其實這是一個回調接口對象,接口裡也有一個handleMessage(msg)方法用來處理消息,
public interface Callback { public boolean handleMessage(Message msg); }
這個對象是在Handler的構造器中傳入的,傳了就有,沒傳就沒有,作用在於有了回調接口來處理消息,就不用寫Handler的子類來實現handleMessage(Message msg)方法了。
如果mCallback為空,或者mCallback.handleMessage(msg)消息沒處理成功,就走Handler類中的handleMessage(msg)了,
public void handleMessage(Message msg) { }
在Handler類中,這是一個空的方法,子類重寫這個方法去完成消息處理的邏輯。
講到這裡,你可能會很奇怪,Handler同時負責發送消息和處理消息,為什麼不直接操作,而是大費周章地先把Message壓入MessageQueue,再取出來處理?其實這體現了程序設計的一個良好的習慣,即“有序性”。如果世界沒有了秩序,那將亂的一塌糊塗,程序也是如此。
3、我們知道,調用Looper.loop()後消息循環處理就開始了,那循環什麼時候退出了?
從loop()方法裡可以看到有一種情況可以退出循環,就是當msg.target == null ;時,但是上面我們講過msg.target的賦值是在sendMessageAtTime(Message msg, long uptimeMillis)方法裡,而不管是Post還是Send發送消息都會走到這個方法,從而給msg.target賦值,所以正常情況下消息處理循環是不會退出的,怎麼辦了?其實在Looper類中有一個quit()方法,我們看下裡面做了什麼事情?
public void quit() { Message msg = Message.obtain(); mQueue.enqueueMessage(msg, 0); }
在這裡面直接往MessageQueue中壓入一條消息,這樣msg.target就為空了,當循環到這條消息時,就退出了循環,所以如果你想終止消息處理循環,請調用這個方法即可。
現在,我們基本上理清了Android中的Handler,MessageQueue、Runnable與Looper之間的關系和它們之間的流程機制了。每個Thread只對應一個Looper;每個Looper只對應一個MessageQueue。把一個Thread比作一座城市,Looper是城市的戶籍名稱;MessageQueue是這座城市的唯一地標性建築;而Hander就是人口,在一座城市,可以有本地人口,也可以有外地人口,但每個人都有戶籍,標識他屬於哪座城市;Message就是貨物,不區分屬地,攜帶在人的身上,在城市或城市間流通。
最後提一點,在Looper類中還有這樣兩個方法:
public static void prepareMainLooper() { prepare(); setMainLooper(myLooper()); myLooper().mQueue.mQuitAllowed = false; } public synchronized static Looper getMainLooper() { return mMainLooper; }
這兩個方法就是我們熟悉的UI主線程專用的方法了,
new Handler(getMainLooper())就代表在主線程運行了。
本文實例講述了Android編程開發之RadioGroup用法。分享給大家供大家參考,具體如下:RadioGroup 有時候比較有用.主要特征是給用戶提供多選一機制。Ma
ATCID主要用來處理PC端傳輸過來的AT命令,從AT命令實際處理的地方來說,主要分為3類: 1. 需要Modem來處理的AT命令; 2. 需要在nativ
需求:近段時間公司有要求寫一個類似於微信發送圖片時,用來選擇照片的一個圖片浏覽器,本來想在網上找一個直接拿來用,找尋無果,只能自己寫了。相信有很多網頁也有這
一、前言最近在學習HTML5相關的知識,發現前端技術的功能越來越強大了,很多功能如果我們通過原生代碼的形式進行實現的話相對於H5會花費數倍的時間,在最求快速迭代的時候是不
Android 程序所依賴一個Library程序B , B 程序中用到&