Handler的執行機制(個人理解)
一. Handler的主要作用
我們都知道,在android裡是不能在子執行緒裡更新UI的, 簡單說一下原因: 更新UI時會呼叫ViewRootImpl.checkThread() 檢查當前執行緒是否與mThread (控制元件初始化時所在的執行緒)是否一致,假如不一致,則丟擲異常,ViewRootImpl.java關鍵程式碼如下:
public ViewRootImpl(Context context, Display display) { mThread = Thread.currentThread(); } void checkThread() { if (mThread != Thread.currentThread()) { throw new CalledFromWrongThreadException( "Only the original thread that created a view hierarchy can touch its views."); } }
Handler的主要作用:在子執行緒中傳送訊息, 在主執行緒中更新UI。
二. Handler的基本使用
1. Handler.sendMessage(msg)
@SuppressLint("HandlerLeak") private Handler mHandler = new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); mTextView.setText(msg.obj + ""); //更新UI } }; //子執行緒傳送訊息 new Thread(new Runnable() { @Override public void run() { Message message = new Message(); message.obj = "6666"; mHandler.sendMessage(message); }).start();
2. Handler.post(msg)
@SuppressLint("HandlerLeak") private Handler mHandler = new Handler(); //子執行緒傳送訊息 new Thread(new Runnable() { @Override public void run() { mHandler.post(new Runnable() { //切換到主執行緒更新UI @Override public void run() { mTextView.setText("123456"); } }); }).start();
二. Handler的執行機制(API28原始碼解讀)
1. Handler.sendMessage(msg)做了些什麼?
Handler.java裡面的方法(部分關鍵程式碼):
sendMessage()→sendMessageDelayed()→sendMessageAtTime()→enqueueMessage;
public final boolean sendMessage(Message msg){ return sendMessageDelayed(msg, 0); } 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; return enqueueMessage(queue, msg, uptimeMillis); } private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; return queue.enqueueMessage(msg, uptimeMillis); }
從上面來看,Handler最終呼叫了MessageQueue.enqueueMessage()
注意:msg.target = this; msg.target 表示當前物件Handler, 後面Looper.loop()用到
MessageQueue.enqueueMessage()
關鍵程式碼如下:
boolean enqueueMessage(Message msg, long when) { msg.when = when; Message p = mMessages; if (p == null || when == 0 || when < p.when) { msg.next = p; mMessages = msg; } else { Message prev; for (;;) { prev = p; p = p.next; if (p == null || when < p.when) { break; } } msg.next = p; prev.next = msg; } } return true; }
從程式碼中慢慢分析,我們可以得出結論:其實MessageQueue.enqueueMessage()方法就是把傳進來的Message訊息物件,按照時間順序、佇列的結構 儲存起來。
在這裡,我們並沒有看到Handler.handleMessage()方法的執行,繼續往下看。
2. 從ActivityThread.main()分析
我們就不慢慢從問題引進來了,直奔主題。
public static void main(String[] args) { Looper.prepareMainLooper();//初始化 Looper.loop(); }
2.1.首先我們看Looper.prepareMainLooper()做了些什麼
prepareMainLooper()→prepare()
public static void prepareMainLooper() { prepare(false); synchronized (Looper.class) { if (sMainLooper != null) {//如果進行第二次初始化,則回報異常 throw new IllegalStateException("The main Looper has already been prepared."); } sMainLooper = myLooper(); //賦值 } } //有且只有一個ThreadLocal物件 static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); private static void prepare(boolean quitAllowed) { //進入ThreadLocal原始碼裡set()和get()可以看出,每個執行緒只儲存一個Looper物件 //所以每個執行緒只能呼叫一次prepare(),否則會拋異常 if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } //ThreadLocal儲存Looper物件 sThreadLocal.set(new Looper(quitAllowed)); }
注意:prepare()方法 這裡有2個關鍵點:
(1) sThreadLocal.set() 和 get()方法
(2) Looper.java構造方法new Looper().
(1). ThreadLocal.java 的set()和get()
public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } return setInitialValue(); } public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); }
set():給當前執行緒賦值一個:Loop物件
get():獲取當前執行緒的Loop物件
(2). Looper.java構造方法
private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }
主要初始化訊息佇列MessageQueue,即:一個Loop有一個MessageQueue訊息佇列物件
總結:Looper.prepareMainLooper() 主要作用:
-
初始化Looper.sThreadLocal.set(new Loop()當前執行緒唯一物件)
-
初始化 MessageQueue物件, new Loop() 對應一個MessageQueue物件
2.2. Looper.loop();
下面是部分的關鍵程式碼
public static void loop() { final Looper me = myLooper(); //獲取唯一的Loop物件 if (me == null) {//假如sThreadLocal.set()沒有賦值,則會丟擲異常 throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); } final MessageQueue queue = me.mQueue;//獲取佇列訊息物件 for (;;) { Message msg = queue.next(); if (msg == null) { return; } try { // 從Handler.enqueueMessage() 方法裡面可以看出,msg.target就是當前的Handler物件 msg.target.dispatchMessage(msg); } msg.recycleUnchecked();//資源快取和回收 } } public static @Nullable Looper myLooper() { return sThreadLocal.get(); }
程式碼裡面都有註釋,最核心程式碼是msg.target.dispatchMessage(msg),msg.target就是當前的Handler物件,
接下來我們來看Handler.dispatchMessage().
public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }
這裡就執行了handleMessage()方法
2.3. 資源回收處理 msg.recycleUnchecked()
private static int sPoolSize = 0; private static final int MAX_POOL_SIZE = 50; void recycleUnchecked() { flags = FLAG_IN_USE; what = 0; arg1 = 0; arg2 = 0; obj = null; replyTo = null; sendingUid = -1; when = 0; target = null; callback = null; data = null; synchronized (sPoolSync) { if (sPoolSize < MAX_POOL_SIZE) { next = sPool; sPool = this; sPoolSize++; } } }
結論:Msg 物件範圍0到50個
最後圖解,如下圖:

Handler機制原理圖.png
三. Handler 常見問題
1. 在子執行緒中建立Handler物件會丟擲異常。
原因:Looper.sThreadLocal.set()沒有初始化ThreadLocalMap物件,導致Handler.mLooper = null;
public Handler(Callback callback, boolean async) { mLooper = Looper.myLooper(); if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread " + Thread.currentThread() + " that has not called Looper.prepare()"); } }
解決方法:
1.先執行Looper.prepare()初始化,一個執行緒只能執行一次。
- 建立Handler物件。
- Looper.loop(); //開始迴圈 傳送佇列訊息 ,如果沒有這一句,是接收不到訊息的。
2. handler.post() 和 sendMessage的區別。
- 寫法不一樣
- post()的msg物件是去快取裡面找的,假如快取沒有就建立一個新的msg物件,
sendMessage的msg物件是從外面傳進來的,可以自定義帶引數。
public final boolean post(Runnable r) { returnsendMessageDelayed(getPostMessage(r), 0); }