在日常開發中,我們經常要用到消息的通信機制,比如網絡請求,在子線程中請求到資料後,切換到主線程(也叫ui線程-activityThread)去更新資料。
在這一過程中,有幾個比較重要的類是我們要熟悉或者了解的,分别是–handler Looper Message Messagequeue ThreadLocal。
這裡我們就不詳細的去分析每個類的底層邏輯了,隻會在要用到的相關方法時再去分析下。
那我們就直接切到正題吧,我們平時需要用到handler的時候絕大部分都是直接在主線程中建立handler并初始化的,然後在子線程中去把資料消息發送到主線程去處理,比如這樣。。
Handler handler=new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case 0:
//todo
break;
}
}
};
發送一條消息
new Thread(new Runnable() {
@Override
public void run() {
//這裡請求網絡資料,然後把資料通過message發送
Message message=Message.obtain();
message.obj="如果不是基本資料類型的可以通過此字段來發送";
message.what=0;
handler.sendMessage(message);
}
}).start();
好了,這是一條很基本的消息發送,很容易了解,接下來,我們去分析為什麼在子線程通過 handler.sendMessage(message);就能把消息給發送到了主線程呢?我們跟着他的方法一步步的下去看,點進handler的sendMessage(message)去看
public final boolean sendMessage(Message msg) {
return this.sendMessageDelayed(msg, 0L);
}
ok,調用了 this.sendMessageDelayed(msg, 0L),這裡的第二個參數是延遲發送的參數,如果我們想延遲發送消息的話,可以一開始直接就調用handler.sendMessageDelayed(),這樣就可以延遲發送消息了。
我們接着點進去sendMessageDelayed()這個方法裡面看看,
public final boolean sendMessageDelayed(Message msg, long delayMillis) {
if(delayMillis < 0L) {
delayMillis = 0L;
}
return this.sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
好吧,判斷了一下延遲的時間,然後調用了this.sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);,接着點進去look look
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = this.mQueue;
if(queue == null) {
RuntimeException e = new RuntimeException(this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
} else {
return this.enqueueMessage(queue, msg, uptimeMillis);
}
}
嗯,主角之一MessageQueue粉墨登場了,讓我們我們來會會它,可以看到這裡直接就把 this.mQueue指派給了新建立的queue了,那 this.mQueue是在哪建立的呢,這是我想寫這篇部落格的原因之一。其實 this.mQueue是主線程的MessageQueue,而非這個子線程自己的MessageQueue,因為主線程在調用main函數之後就已經建立好了MessageQueue,具體我們看下代碼
public static void main(String[] args) {
Trace.traceBegin(64L, "ActivityThreadMain");
CloseGuard.setEnabled(false);
Environment.initForCurrentUser();
EventLogger.setReporter(new ActivityThread.EventLoggingReporter(null));
File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
TrustedCertificateStore.setDefaultUserDirectory(configDir);
Process.setArgV0("<pre-initialized>");
> Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if(sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
Trace.traceEnd(64L);
> Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
這裡先說下,其實MessageQueue是Looper的一個成員變量,它在Looper的構造函數中初始化,當調用Looper.prepare時
public static void prepare() {
prepare(true);
}
-》來到 private static void prepare(boolean quitAllowed) {
if(sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
} else {
sThreadLocal.set(new Looper(quitAllowed));
}
}
這裡可以看到Looper是在 sThreadLocal.set(new Looper(quitAllowed));中被建立,這個sThreadLocal是ThreadLocal,也是我們的主角之一,它主要是用來存儲線程内變量的,接下來我們進去Looper的構造函數中看。
private Looper(boolean quitAllowed) {
this.mQueue = new MessageQueue(quitAllowed);
this.mThread = Thread.currentThread();
}
嗯哼,終于看到,原來this.mQueue是在這裡被建立的,到這裡我們先捋一下思路,調用了Looper.prepare之後才建立MessageQueue,但是我們好像從來沒有調用過Looper.prepare()函數呀,那MessageQueue是哪來的呀,别急,慢慢來,喝口水消消性子,等貧僧念念經。。。
public Handler() {
this((Handler.Callback)null, false);
}
public Handler(Handler.Callback callback) {
this(callback, false);
}
public Handler(Looper looper) {
this(looper, (Handler.Callback)null, false);
}
public Handler(Looper looper, Handler.Callback callback) {
this(looper, callback, false);
}
public Handler(boolean async) {
this((Handler.Callback)null, async);
}
public Handler(Handler.Callback callback, boolean async) {
this.mLooper = Looper.myLooper();
if(this.mLooper == null) {
throw new RuntimeException("Can't create handler inside thread that has not called Looper.prepare()");
} else {
this.mQueue = this.mLooper.mQueue;
this.mCallback = callback;
this.mAsynchronous = async;
}
}
這裡看到handler有幾個構造函數,我們建立handler的時候并沒有傳入任何參數,是以調用了handler的無參構造函數,然後無參構造函數又調用有兩個參數的那個構造函數 public Handler(Handler.Callback callback, boolean async),然後看到這行 this.mLooper = Looper.myLooper();
看到沒,Looper是在這裡被指派的,我們進去Looper.myLooper()這個方法看
public static Looper myLooper() {
return (Looper)sThreadLocal.get();
}
哦,原來Looper是在這裡獲得的,那為什麼通過(Looper)sThreadLocal.get()就能獲得呢,因為我們說過ThreadLocal是專門存儲線程内資訊的,而Looper也屬于線程内的變量,當然也就被存儲在ThreadLocal裡面咯,而在每個線程内調用(Looper)sThreadLocal.get()所獲得的值是不同的,因為在(Looper)sThreadLocal.get()的内部會通過Thread.currentThread去擷取每個線程對應的值,這裡我們就不貼代碼了,有需要了解的爺請自行檢視源碼哈,這裡請允許小弟偷下懶呗。
接着回到話題,因為我們是在主線程建立的handler,是以理所當然的最後擷取到的Looper就可以一口咬定是屬于主線程的啦,而更理所當然的MessageQueue也是屬于主線程的啦,因為它是在Looper的構造函數中被建立的嘛!那麼,通過handler.sendMessage()發送的message自然就是存進主線的MessageQueue中咯,而在
Handler handler=new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case 0:
//todo
break;
}
}
};
中取出的message也是從主線程的MessageQueue取出的,接下來我們回到上面的步伐接着看,
剛剛我們看到
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = this.mQueue;
if(queue == null) {
RuntimeException e = new RuntimeException(this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
} else {
return this.enqueueMessage(queue, msg, uptimeMillis);
}
}
看到調用了this.enqueueMessage(queue, msg, uptimeMillis)這個方法,點進去看看
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if(this.mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
可以看到,把handler本身指派給了message的target字段,因為這個方法是在handler類裡面調用的,是以this就代表了handler本身。然後看到queue.enqueueMessage(msg, uptimeMillis);這裡的queue就是從主線程所擷取來的MessageQueue,就是在這個方法中插入一條消息到消息隊列中的。好了,消息存進去了,那麼如何取出消息的呢?
取出消息的操作是在Looper.loop中,我們去看看
public static void loop() {
Looper me = myLooper();
if(me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
} else {
MessageQueue queue = me.mQueue;
Binder.clearCallingIdentity();
long ident = Binder.clearCallingIdentity();
while(true) {
Message msg = queue.next();
if(msg == null) {
return;
}
Printer logging = me.mLogging;
if(logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what);
}
long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
long traceTag = me.mTraceTag;
if(traceTag != 0L && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
long start = slowDispatchThresholdMs == 0L?0L:SystemClock.uptimeMillis();
long end;
try {
msg.target.dispatchMessage(msg);
end = slowDispatchThresholdMs == 0L?0L:SystemClock.uptimeMillis();
} finally {
if(traceTag != 0L) {
Trace.traceEnd(traceTag);
}
}
long newIdent;
if(slowDispatchThresholdMs > 0L) {
newIdent = end - start;
if(newIdent > slowDispatchThresholdMs) {
Slog.w("Looper", "Dispatch took " + newIdent + "ms on " + Thread.currentThread().getName() + ", h=" + msg.target + " cb=" + msg.callback + " msg=" + msg.what);
}
}
if(logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
newIdent = Binder.clearCallingIdentity();
if(ident != newIdent) {
Log.wtf("Looper", "Thread identity changed from 0x" + Long.toHexString(ident) + " to 0x" + Long.toHexString(newIdent) + " while dispatching to " + msg.target.getClass().getName() + " " + msg.callback + " what=" + msg.what);
}
msg.recycleUnchecked();
}
}
}
這段代碼有點長,我們挑重點來看就行了,其實在看源碼的時候,我們沒有必要把每行代碼都看懂,隻要看明白大概流程就行了,因為有些源碼不是一個人寫的,你很難去揣摩别人的每一行代碼的作用,那樣你會走火入魔的(不恰當),不扯了,回到主題
MessageQueue queue = me.mQueue;
這裡首先擷取到了我們存進去消息的那個MessageQueue,然後就進入到了阻塞循環體,我們看重點
Message msg = queue.next();
if(msg == null) {
return;
}
在循環體中通過queue.next();取出消息(同時把該message從消息隊列中移除),這裡可以看到,當msg ==null時,就不會往下走了,在這裡就阻塞住了,接着往下看
try {
msg.target.dispatchMessage(msg);
end = slowDispatchThresholdMs == 0L?0L:SystemClock.uptimeMillis();
} finally {
if(traceTag != 0L) {
Trace.traceEnd(traceTag);
}
}
看這句 msg.target.dispatchMessage(msg);這裡的 msg.target就是我們先前發送消息的時候傳入的handler對象,點進去dispatchMessage(msg)方法看看
public void dispatchMessage(Message msg) {
if(msg.callback != null) {
handleCallback(msg);
} else {
if(this.mCallback != null && this.mCallback.handleMessage(msg)) {
return;
}
this.handleMessage(msg);
}
}
看這句msg.callback != null,這裡的callback其實是Runnable對象,當我們通過handler.post系列函數發送消息的時候,這裡的callback就不會為null,但我們是通過send系列函數發送的,是以這裡它為空,接着進入到
else {
if(this.mCallback != null && this.mCallback.handleMessage(msg)) {
return;
}
this.handleMessage(msg);
}
this.mCallback != null && this.mCallback.handleMessage(msg)這裡的mCallback是Handler.Callback mCallback;如果我們建立handler時傳入Callback,它就不會為空,還記得handler有這樣一個構造函數吧
public Handler(Handler.Callback callback) {
this(callback, false);
}
是以它也是空,接着肯定的要調用 this.handleMessage(msg);方法,我們進去看
public void handleMessage(Message msg) {
}
看到沒,空空如也。。。
這個方法就是我們建立handler時重寫的
Handler handler=new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case 0:
//todo
break;
}
}
};
終于,經過了九九六十一,回到了主線程的
public void handleMessage(Message msg) {
switch (msg.what){
case 0:
//todo
break;
}
handleMessage(Message msg)方法中,這裡就可以就行ui重新整理的工作啦,咧咧咧,我不聽,我不聽,我就要在子線程重新整理
額。陛下,冷靜點,到飯點了,我們吃飯去吧。。。。。。。。
最後說明一點,本文純屬個人了解,可能不可避免有錯誤,各位看官如果發現有錯誤,請給指出啊,别有誤人子弟我就要跳黃河洗不清啦,哈哈哈。。。。