我的概念裏面handler用來更新UI。一直有一個問題困惱我,爲何我在主線程裏面建立一個Handler不須要傳遞傳遞Looper,而在一個子線程裏面必須調用Looper.prepare, Looper.loop。今天看了看源碼,終於知道里面的原委。我的以爲一切和ThreadLocal有關,關於ThreadLocal,請閱讀以下博客:Android的消息機制之ThreadLocal的工做原理。 簡而言之,ThreadLocal和當前線程綁定,若是handler在UI線程裏面建立,ThreadLocal已經和主線程綁定,即便handler傳入爲空,也能夠拿到主線程的looper,代碼以下,android
mLooper = Looper.myLooper(); if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); } public static @Nullable Looper myLooper() { return sThreadLocal.get(); }
咱們知道,在app啓動時候,在ActivityThread裏面的main函數被執行:
Looper.prepareMainLooper(); ActivityThread thread = new ActivityThread(); thread.attach(false); if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler(); } if (false) { Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread")); } // End of event ActivityThreadMain. Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); Looper.loop();
綜合上面代碼,looper已經在app啓動的時候建立,因此 Looper.myLooper()取得的是UI主線程對應的looper,沒有錯誤拋出。若是handler實在子線城裏面建立:
new Thread(new Runnable() { @Override public void run() { Handler handler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); } }; } }).start();
能夠很容易得出結論,子線程沒有和ThreadLocal綁定,因此ThreadLocal裏面的looper爲空,以下異常拋出:
if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); }
既然有上面問題,如何在子線程裏面建立handler呢?
new Thread(new Runnable() { @Override public void run() { Looper.prepare(); Handler handler = new Handler(Looper.myLooper()) { public void handleMessage(android.os.Message msg) { // XXX } }; Looper.loop(); } }).start();
繼續看Looper.prepare:
private static void prepare(boolean quitAllowed) { if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(quitAllowed)); }
ThreadLocal的set方法以下, 能夠看到內部ThreadLocalMap已當前線程爲key進行綁定。app
public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); }
這樣當前線程和 sThreadLocal就綁定了,Looper.myLooper()獲得的就是上面建立的 new Looper(quitAllowed)。
固然聰明的google意識到這個問題,因此有個HandlerThread能夠省去手動調用prepare和loop的煩惱,核心代碼以下:
public class HandlerThread extends Thread { @Override public void run() { mTid = Process.myTid(); Looper.prepare(); synchronized (this) { mLooper = Looper.myLooper(); notifyAll(); } Process.setThreadPriority(mPriority); onLooperPrepared(); Looper.loop(); mTid = -1; } }