Handler使用小結

我的概念裏面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;
    }
    
}
相關文章
相關標籤/搜索