Handler、Looper原始碼學習
Android中,使用Handler,Looper的場景主要是跨執行緒通訊,先看一個簡單的例子:
public class MainActivity extends Activity { public final static int MESSAGE_TYPE_1 = 1; private WorkHandler mHandler; private static class WorkHandler extends Handler { public WorkHandler(Looper looper) { super(looper); } public void handleMessage(Message msg) { switch (msg.what) { case MESSAGE_TYPE_1: LogUtils.d("this is message 1"); // do something... break; default: break; } } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); HandlerThread thread = new HandlerThread("work_thread"); // 建立一個執行緒物件 thread.start(); // 啟動子執行緒,這時會利用HandlerThread#run方法,建立一個Looper物件,繫結子執行緒,執行緒的執行並進入loop迴圈模式 mHandler = new WorkHandler(thread.getLooper()); // 建立一個Handler物件,並傳入Looper Message msg = new Message(); msg.what = MESSAGE_TYPE_1; mHandler.sendMessage(msg); // 通過主執行緒傳送1個訊息,訊息被壓入messageQueue(所屬於Looper物件) } }
這裡涉及到4個重要的類:Handler,Looper,MessageQueue(Handler和Looper的成員變數),HandlerThread。
1. Handler
public class Handler { final Looper mLooper; final MessageQueue mQueue; public Handler(Looper looper, Callback callback, boolean async) { mLooper = looper; mQueue = looper.mQueue; // ... } }
Handler類有2個成員變數,mLooper和mQueue,從建構函式可看出,mQueue用的也是Looper的成員變數。所以本質上Handler含有1個Looper物件。
Handler有2個重要的方法,我們經常用到,sendMessage和handleMessage。
-
sendMessage的真正實現是enqueueMessage方法,如下:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this;// 重要步驟1. 將此Handler物件繫結到此Message物件,以便將來Looper.loop方法中呼叫目標Handler物件來處理此訊息 if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }
將一條新訊息Message壓入MessageQueue物件(即mQueue,所屬於mLooper),預設是同步,Handler初始化時可設定訊息傳送是否非同步 - handleMessage是處理訊息的函式,由dispatchMessage方法所呼叫,一般應用使用時重寫handleMessage函式,定製自己的訊息處理邏輯
2. Looper
Looper類有2個重要的成員變數,mQueue和mThread,另外還有1個所屬於程序的sThreadLocal集合,ThreadLocal<Looper>本質是一個Map資料結構,key是執行緒物件,value是Looper物件。它的作用是儲存程序中所有<Thread,Looper>的資訊,保證Looper物件和執行緒1:1對應。
public final class Looper { static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); final MessageQueue mQueue; final Thread mThread; // ... }
Looper有3個重要的靜態方法:prepare,prepareMainLooper,loop。
- prepare方法是為本執行緒建立一個Looper物件,並插入sThreadLocal中儲存
private static void prepare(boolean quitAllowed) { if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thLooper.prepare,Looper.loop等read"); } sThreadLocal.set(new Looper(quitAllowed)); }
- prepareMainLooper方法是給ActivityThread#main方法呼叫的,程序啟動時最先執行的入口函式裡,完成主執行緒Looper的初始化。因為時機是最早的,如果應用在啟動完成後,再呼叫此方法,會丟擲異常
public static void prepareMainLooper() { prepare(false); // false代表引數quitAllowed的值,表示不允許退出 synchronized (Looper.class) { if (sMainLooper != null) { throw new IllegalStateException("The main Looper has already been prepared."); } sMainLooper = myLooper(); } }
- loop方法的核心是一個無限迴圈,不斷從mQueue中取出新訊息,呼叫目標Handler#dispatchMessage方法來處理訊息,其中dispatchMessage裡就會呼叫Handler#handleMessage方法
public static void loop() { // ... for (;;) { Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } // ... msg.target.dispatchMessage(msg); // ... } }
3. MessageQueue
MessageQueue的實現較為複雜,主要是利用JNI,呼叫native相關方法,例如MessageQueue初始化時,呼叫nativeInit();
程式碼路徑:frameworks/base/core/jni/android_os_MessageQueue.cpp
這裡暫時先不深入研究,後面再補充。
4. HandlerThread
HandlerThread的實現很簡單,繼承於Thread類。作用是封裝一個預設帶Looper的Thread類,省去開發者自己呼叫Looper.prepare,Looper.loop等方法,使Handler的使用更方便。如本文開始的例子。
HandlerThread的核心方法是重寫的run方法,其中會呼叫Looper.prepare,Looper.loop等。
總結:
-
1條訊息的生命週期總結如下,簡單分為3步:
handler.sendMessage(msg)--執行緒A傳送訊息,訊息壓入messageQueue----> looper.loop --執行緒B從messageQueue中取出訊息----> handler.handleMessage(msg)--執行緒B消費掉此訊息----> 結束 -
Handler,Looper,MessageQueue,從物件角度所屬的關係:
handler <----- looper <----- messageQueue -
Looper#quit和Looper#quitSafely方法有何區別?
a. Looper#quit -> mQueue#quit(false) -> mQueue#removeAllMessagesLocked
直接終止Looper,不再處理所有訊息
b. Looper#quitSafely -> mQueue#quit(true) -> mQueue#removeAllFutureMessagesLocked
處理完MessageQueue中已有訊息,再終止Looper