前言
handler是Android中獨特的線程間通信方式.我們使用handler的經典模式是,在主線程中創建一個handler并重寫其handlerMessage方法,然后在子線程中發送消息讓handler進行處理.下面,就來看看這個handler里面到底是怎么實現的.
@TODO:handler經典用法
創建Handler
//Handler的構造函數
public Handler(Callback callback, boolean async) {
.....代碼省略...
mLooper = Looper.myLooper(); //取出looper對象
if (mLooper == null) { //判斷looper是否為空
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");//
}
mQueue = mLooper.mQueue; //取出looper的MessageQueue對象
mCallback = callback;
mAsynchronous = async;
}
其中,myLooper()方法,最終調用了sThreadLocal.get()方法,從sThreadLocal中取出Looper.
sThreadLocal是一個ThreadLocal對象,它可以保證每個線程中只有一個Looper對象.關于ThreadLocal具體可以參考這篇文章.
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
接下來,判斷mLooper對象是否為空,為空則拋出異常:"如果沒有調用Looper.prepare()方法,則不能創建Handler"
為什么呢?我們一般在主線程創建handler的時候,并沒有調用過prepare方法,
//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) {
.....代碼省略....
sThreadLocal.set(new Looper(quitAllowed));
}
//ActivityyThread 類
public static final void main(String[] args) {
SamplingProfilerIntegration.start();
……
Looper.prepareMainLooper();
}
那為什么沒有拋出這個異常呢?
我們看到ActivityThread中,已經調用了looper.prepareMainLooper()方法創建一個looper對象并放到容器sThreadLocal中.
另外,在創建looper的同時創建了一個消息隊列.
創建消息
一般通過message.obtain(),從message pool中,取出復用的空消息,并給msg設置相應的數據obj,標識what,when等屬性.
message主要是對我們發送的消息進行此一次封裝.
handler發送消息到消息隊列
創建完消息后,我們會調用handler.sendMessage()方法,將消息發送到消息隊列,而sendmessage方法最終調用enqueueMessage方法,
首先調用msg.target = this;把當前的handler對象賦值給msg的target,這里就實現了handler與msg的綁定,后續在消息分發給handler處理的時候,就知道該由哪個handler來處理了.
至于enqueueMessage方法,主要是將消息列表里面的第一條消息取出,進行時間的對比排序.
public final boolean sendMessageAtFrontOfQueue(Message msg) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, 0);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
Looper輪詢器
剛才在ActivityThread中,在Looper.prepareMainLooper()方法之后,還調用了Looper.loop()方法
為什么突然講到這個呢?我們將消息放到請求隊列之后,需要調用looper的loop方法,不斷從消息隊列中取出消息,并分發給對應的handler來執行.
public static void loop() {
final Looper me = myLooper();
......
final MessageQueue queue = me.mQueue;
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
return;
}
Printer logging = me.mLogging;
msg.target.dispatchMessage(msg);//分發消息給對應的handler
}
//Handler類:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg); //這里就是我們重寫的handleMessage方法
}
}
Loop()方法中的主要邏輯是不斷從消息隊列中取出消息,并將消息分發給對應的handler.
然后會調用handler的dispatchMessage方法,處理消息.至此,全過程過程完成.
注意了,當我們在子線程中創建handler時,除了需要手動調用Looper.prepare()方法之外,還需要調用Looper.loop()方法開啟輪詢.
異步消息處理機制
Handler進行消息處理的模式,總體來說如下:
1.Handler發送消息到消息隊列MessageQueue,并創建Looper開啟輪詢,
2.Looper對象不停遍歷MessageQueue,從消息隊列中取出消息.分發給對應的handler
3.handler獲得消息后,調用handleMessage方法處理消息.
擴展: HandlerThread
@HydraTeam 出品 轉載請注明出處