上篇文章介紹了幾種hanlder建立方式,其實這種使用方式你們都知道,可是爲何能夠這麼作,可能不少人不知道,至少不清楚,網上不少文章也是處處粘貼,聽別說handler把Message發送到MessageQueue裏面去,Looper經過死循環,不斷從MessageQueue裏面獲取Message處理消息,由於Mesage.target就是當前hanlder,因此最後轉到handleMessage()方法中去處理,整個流程是這樣。其實大概都是對的,以前面試的時候,我也都是這麼說,也沒有面試官深刻問過,此次正好有時間深刻源碼系統學習下,畢竟仍是要知其因此然。android
package com.example.test.myapplication;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.widget.Toast;
public class MainActivity extends Activity {
private Handler handler1 = new Handler() {
@Override
public void handleMessage(Message msg) {
if (msg.arg1 == 1) {
Toast.makeText(MainActivity.this, "hanlder1", Toast.LENGTH_SHORT).show();
}
super.handleMessage(msg);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Thread(new Runnable() {
@Override
public void run() {
Message message = handler1.obtainMessage();
message.arg1 = 1;
handler1.sendMessage(message);
}
}).start();
}
}
複製代碼
這種用法是你們最經常使用的,下面咱們就用這個來做爲切入點。面試
1)Looper,MessageQueue建立 應用啓動的時候會先調用ActivityThread的main方法,main方法會調用Looper.prepareMainLooper();建立Looper對象,Looper對象的構造方法中會建立MessageQueue對象,源碼以下所示:bash
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
SamplingProfilerIntegration.start();
// CloseGuard defaults to true and can be quite spammy. We
// disable it here, but selectively enable it later (via
// StrictMode) on debug builds, but using DropBox, not logs.
CloseGuard.setEnabled(false);
Environment.initForCurrentUser();
// Set the reporter for event logging in libcore
EventLogger.setReporter(new EventLoggingReporter());
AndroidKeyStoreProvider.install();
// Make sure TrustedCertificateStore looks in the right place for CA certificates
final 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();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
複製代碼
在第23行能夠看到Looper.prepareMainLooper();方法,此方法其實就是UI線程默認爲應用建立Looper對象,咱們繼續看下prepareMainLooper()方法的源碼:app
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));
}
複製代碼
這裏經過調用調用prepare()方法,建立了Looper對象,並且sThreadLocal若是沒有Looper,則新建Looper進去,若是存在,則拋出異常,並且從判空能夠看出一個線程最多隻能建立一個Looper對象,驗證了不少人說的一個線程一個Looper對象的說法。 上面提到在Looper的構造方法中,會建立MessageQueue對象,咱們看一下Looper構造方法:less
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
複製代碼
沒有錯,Looper建立的同時會建立MessageQueue,因此不少人說一個Looper對應一個MessageQueue就是從這裏來的。Looper跟MessageQueue都建立好了,接下來看如何發送Message消息。async
2)handler.sendMessage(message)發送消息到什麼地方,內部怎麼處理 要想知道發送到哪裏,怎麼處理,只有一條路,跟到源碼中去看:ide
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
複製代碼
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
複製代碼
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
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, uptimeMillis);
}
複製代碼
前面兩個方法就不看了,直接看sendMessageAtTime()方法,sendMessageAtTime()方法中傳入兩個參數,msg就是咱們handler.sendMessage(message)發送的Message對象,而uptimeMillis參數則表示發送消息的時間,它的值等於自系統開機到當前時間的毫秒數再加上延遲時間,若是你調用的不是sendMessageDelayed()方法,則延遲時間就爲0。方法最後調用了enqueueMessage()。函數
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
複製代碼
上面方法中,會調用queue.enqueueMessage(msg, uptimeMillis),queue其實就是Looper構造函數中初始化的MessageQueue對象,能夠看到,handler把消息發送到MessageQueue中了。oop
3)MessageQueue對象 接下來咱們看queue.enqueueMessage(msg, uptimeMillis);的源碼:post
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) {
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// Inserted within the middle of the queue. Usually we don't have to wake // up the event queue unless there is a barrier at the head of the queue // and the message is the earliest asynchronous message in the queue. needWake = mBlocked && p.target == null && msg.isAsynchronous(); Message prev; for (;;) { prev = p; p = p.next; if (p == null || when < p.when) { break; } if (needWake && p.isAsynchronous()) { needWake = false; } } msg.next = p; // invariant: p == prev.next prev.next = msg; } // We can assume mPtr != 0 because mQuitting is false. if (needWake) { nativeWake(mPtr); } } return true; } 複製代碼
這段代碼有點意思,其實就是入隊操做,以前一直覺得MessageQueue裏面有個列表,Message依次保存在裏面,其實並非。MessageQueue藉助Message對象(next)成員變量,實現單向鏈表,而且用一個mMessages對象表示當前待處理的消息。 咱們來分析一下上面代碼,第二十行,首先Message p = mMessages;把當前待處理的message賦值給p,接下來是一個很長的判斷if (p == null || when == 0 || when < p.when),根據判斷能夠得知,有如下兩種狀況會執行if裏面的語句:1.應用首次調用sendMessage時,當前待處理的消息爲null,則p爲空,則會執行;2.當前傳入msg的when小於當前待處理msg的when,及當前傳入的消息要先於當前待處理mMessages獲得處理,因此傳入的msg賦值爲mMessages,而下一個待處理消息才爲原先的待處理消息。
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
}
複製代碼
當執行過多個sendMessage方法時而且消息的時間(when)比當前待處理的消息的時間(when)大,這個時候就會移動鏈表,根據時間前後,插入到合適的位置,調用以下代碼:
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
複製代碼
不停的循環,根據when,及發送時間來前後排序,先發送的排在最前面,用message.next來指定下一個Message對象,這樣MessageQueue經過時間把多個Message組成一個單向鏈表。
4)Looper對象,取當前待處理Message。 有進就有出,如何從MessageQueue中獲取當前Message進行處理呢,這就要看Looper.loop()方法了。這裏能夠明白爲何子線程中初始化Handler最後爲何要顯示調用Looper.loop()方法了吧,其實至關於啓動獲取Message方法的循環,主線程中會默認調用。
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// This must be in a local variable, in case a UI event sets the logger
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
msg.target.dispatchMessage(msg);
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted. final long newIdent = Binder.clearCallingIdentity(); if (ident != newIdent) { Log.wtf(TAG, "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(); } } 複製代碼
能夠看到,第13行依然是一個死循環,不斷執行的queue.next()方法,next()方法其實就是作消息出隊列操做。獲取當前待處理的mMessages對象,而後讓下一條消息成爲mMessages,其實跟鏈表操做同樣,沒有難度。loop()方法的第27行,Message獲取到後,則調用msg.target.dispatchMessage(msg)方法,msg是Message對象,target是什麼呢?看下Message源碼,target實際上是發送該Message的Handler對象。 調用Message message = handler1.obtainMessage();的時候,會進行賦值:
public static Message obtain(Handler h) {
Message m = obtain();
m.target = h;
return m;
}
複製代碼
接下來固然就要看一看Handler中dispatchMessage()方法的源碼了,以下所示:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
複製代碼
通常狀況下,按照咱們上面的調用方式,msg.callback,mCallback 都爲空,則會直接調用handleMessage(msg)方法,這樣終於轉到咱們的handleMessage方法中來了,在裏面去作更新UI的操做。固然有人會問,何時msg.callback,mCallback不爲空呢? msg.callback不爲空,其實只要看看源碼裏面,哪些方法能夠賦值就能夠了:
public static Message obtain(Handler h, Runnable callback) {
Message m = obtain();
m.target = h;
m.callback = callback;
return m;
}
複製代碼
下面是個人調用方法:
package com.example.test.myapplication;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
public class MainActivity extends Activity {
private Handler handler1 = new Handler();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Thread(new Runnable() {
@Override
public void run() {
Message message = Message.obtain(handler1, new Runnable() {
@Override
public void run() {
System.out.println("handler2 Thread========="+Thread.currentThread().getName());
}
});
message.arg1 = 1;
handler1.sendMessage(message);
}
}).start();
}
}
複製代碼
從打印信息能夠看到,run方法裏面其實也是主線程,這種方式其實不少,下面一一介紹。
上面這個callback的調用方式你們有沒有很熟悉呢,其實有幾種是你們平時常用的:
1.Handler的post()方法 2.View的post()方法 3.Activity的runOnUiThread()方法 這些均可以在子線程中轉到主線程,從而去更新UI,至於爲何能夠呢,相信不少人不知道。 1)Handler的post()源碼
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
複製代碼
萬變不離其宗,其實仍是調用sendMessageDelayed方法,並且Runnable還被封裝成了Message,後面入鏈表,出鏈表過程跟前面講的都同樣。
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
複製代碼
只是最後的時候,msg.callback != null,他會調用handleCallback(msg);
private static void handleCallback(Message message) {
message.callback.run();
}
複製代碼
這樣,run方法被調用,是否是很簡單。
2)View的post()源碼 View類中,post方法:
public boolean post(Runnable action) {
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
return attachInfo.mHandler.post(action);
}
// Assume that post will succeed later
ViewRootImpl.getRunQueue().post(action);
return true;
}
複製代碼
居然仍是mHandler.post(action),原理同上了。
3)Activity的runOnUiThread()源碼
public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
mHandler.post(action);
} else {
action.run();
}
}
複製代碼
若是當前的線程不等於UI線程,仍然執行mHandler.post(action)方法,若是就在主線程,固然直接接口回調,調用run方法就能夠了。
因此無論何種子線程轉到主線程去刷新UI,背後原理其實都是同樣的,搞清楚了一個,都清楚了。但願看到這篇文章後,面試官再問子線程更新主線程的問題的時候,你能夠是下面的表情:
若有錯誤歡迎指出來,一塊兒學習。