Handler 是什麼?這個是android面試官最喜歡問的問題,用腳趾都能回答出來,Handler可用於android多線程通信,可以用來更新UI的,用來發送message,處理message的。於是在很多地方我們都直接new 一個Handler 來使用,那麼問題來了,如果你在一個自定義線程中new一個Handler,這個Handler能用麼?能 or 不能。 Why?
先給大家上個圖,好有個整的印象:
呵呵,現在老鳥帶你揭開Handler這個神祕的面紗,大家都知道Handler 是用來發送和處理message的,那麼message存在哪裏呢?答案是MessageQueue, Handler發送的message當然要放在MessageQueue了,那麼問題又來了,MessageQueue中的message誰在管理,是Handler? NO, NO, 是Looper。 下面我們看看Handler的部分源碼:
public Handler() {
this(null, false);
}
public Handler(Callback callback, boolean async) {
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
看到這裏是不是豁然開朗了,Handler需要一個Looper, Looper裏面有一個mQueue, 這個mQueue就是MessageQueue,不信?你去看Looper.java源碼。所以自定義線程中是不能就這麼簡單的new一個Handler來用滴。如果沒有這個Looper,在運行時是會拋出異常滴。那麼問題又來了,爲啥我們在Activity或Fragment裏面可以直接new一個Handler來用呢?答案是:因爲應用的主線程幫我們定義好了這個Looper,我們在Activity或Fragment裏面new一個Handler都是使用主線程的Looper。
呵呵,說到主線程,是不是又讓人蒙啦,大名鼎鼎的android主線程,就是UI線程,老鳥在這裏給你簡單介紹下吧,更詳細的關於主線程,老鳥會單獨N篇文章講解。首先,主線程就是ActivityThread, 每一個應用有且只有一個主線程,UI改變都是在這個AcitivityThread裏面完成的。在這裏我們只關心ActivityThread是如何創建主線程Looper的,下面是ActivityThread部分源碼:
public static void main(String[] args) {
Environment.initForCurrentUser();
Process.setArgV0("<pre-initialized>");
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
ActivityThread的main方法裏會調用Looper.prepareMainLooper方法,這個方法就是創建主線程的Looper, 並且後面執行了Looper.loop(),這個loop()就是一個for(;;)永久循環,這個循環體裏面就是不斷判斷是否有新的message進入了Looper的MessageQueue裏面,如果有message,就把他發送給發送這個message的handler去執行。當我們在activity或fragment裏面new一個Handler的時候,其實都是拿到主線程這個Looper,然後發消息到這個Looper的MessageQueue裏。那麼我們在activity裏面是怎麼拿到這個主線程Looper的呢?在上面的Handler代碼裏我們new一個Handler的時候,會調用Looper.myLooper(),下面看看Looper的部分代碼:
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
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));
}
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
由上面代碼可以知道prepareMainLooper調用了prepare(), prepare()裏面執行了new Looper(), 這裏新建了一個MessageQueue, 最後並且把這個looper存入了sThreadLocale裏面。所以當我們在activity或fragment裏面new Handler的時候,都會從myLooper()裏面拿到這個主線程looper啦。
那麼又有問題來了,我們自己定義的thread裏面我也想用Handler怎麼辦?這個問題現在是不是很簡單了?就是我們自己也給我們的線程生成一個Looper,不就OK了麼,對滴,就這樣。上面調用Looper的prepare()就會爲我們生成一個Looper。如何把這個looper跟我們的Handler關聯起來呢,我們可以用調用Handler(Looper looper)這樣,把他們關聯起來,最後調用Looper.loop()讓這個looper動起來,這樣你就可以用這個Handler發送處理message啦。是不是很爽。
class MyLooperThread extends Thread{
private final String TAG = "MyLooperThread";
private MyHandler mHandler;
public MyLooperThread(){
}
@Override
public void run() {
super.run();
Looper.prepare();
mHandler = new MyHandler(Looper.myLooper());
Looper.loop();
}
public void sendMessage(Message msg){
mHandler.sendMessage(msg);
}
class MyHandler extends Handler{
MyHandler(Looper looper){
super(looper);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case 1:
Log.i(TAG,"1111111111111");
break;
default:
}
}
}
}
上面我們就可以在handleMessage裏面處理我們自己的消息啦。
說了這麼多我們還沒說HandlerThread, 這個又是什麼鬼呢?其實這個就是android幫我們封裝了Looper的一個thread,它的實現就跟我們上面自己的MyLooperThread差不多,使用它我們就不用自己去手動調用生成Looper的代碼啦,這是不是很貼心呢。