我的概念里面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; } }