Handler Looper Message的原始碼分析
Handler
Handler是用來分發和處理訊息的,通常我們建立Handler都是使用其無引數的構造方法
public Handler() {
this(null, false);
}
其內部呼叫的是2個引數的構造方法
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass. isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
//大概的意思就是建立Handler的匿名內部類,成員類,區域性類的時候會給出下面log的提示
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
//獲取當前執行緒的Looper物件,如果是在UI執行緒(四大元件)中建立Handler,那麼這裡拿到的就是主執行緒的Looper
mLooper = Looper.myLooper();
if (mLooper == null) {
//在子執行緒中建立Handler如果沒有事先初始化Looper,是拿不到Looper的,因此會拋異常
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()" );
}
//記錄Looper物件內的MessageQueue訊息佇列
mQueue = mLooper.mQueue;
//記錄傳遞進來的callback介面,注意這裡的Callback介面是定義在Handler類內部的.
mCallback = callback;
//記錄是否是非同步,預設構造方法傳入的是false,表示同步處理訊息
mAsynchronous = async;
}
//內部介面
public interface Callback {
public boolean handleMessage(Message msg);
}
分析上面的原始碼可以發現,在new Handler的時候,內部會得到一個Looper物件,以及該Looper的MessageQueue物件.
同時提到了 "Can't create handler inside thread that has not called Looper.prepare()");
這個異常,僅當Looper獲取為空的時候丟擲.
另外,引數async表示此建立的Handler是否用於處理非同步訊息,如果是則需要通過Message的方式來非同步處理,而如果不是,那就通過postRunnable的方式來處理同步的訊息.
Looper
上面建立handler的時候提到了Looper,那麼現在我們來分析下Looper的原始碼
public final class Looper {
private static final String TAG = "Looper";
// 通過ThreadLocal繫結Looper物件到當前執行緒,預設情況下sThreadLocal.get()會返回null,除非你事先呼叫了prepare()方法.
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
// 主執行緒的Looper物件,在主執行緒中建立Handler,預設會初始化一個Looper與主執行緒繫結
private static Looper sMainLooper;
//訊息佇列,每個Looper物件內部都有一個訊息佇列
final MessageQueue mQueue;
//當前執行緒
final Thread mThread;
//log輸出物件
private Printer mLogging;
/**
* 初始化Looper,給當前執行緒繫結一個Looper物件,該方法建立的Looper,預設是支援結束loop的
*/
public static void prepare() {
prepare(true);
}
/**
* 私有的初始化Looper物件方法
* 引數quitAllowed表示是否支援結束loop,通過呼叫quit()或者quitSafely()方法進行結束
*/
private static void prepare(boolean quitAllowed) {
//一個執行緒只能繫結一次Looper
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
//建立一個Looper,並將Looper繫結到當前執行緒中
sThreadLocal.set(new Looper(quitAllowed));
}
/**
* 私有構造方法,因此建立Looper必須要通過prepare方法建立
*/
private Looper(boolean quitAllowed) {
//初始化該Looper的MessageQueue物件,Handler傳送訊息的時候會用到mQueue,它用於儲存Handler傳送的Message
mQueue = new MessageQueue(quitAllowed);
//獲取當前執行緒,作為sThreadLocal的key,value就是該繫結的Looper物件
mThread = Thread.currentThread();
}
/**
*初始化主執行緒的Looper物件,由android系統呼叫,不需要我們自己呼叫
*/
public static void prepareMainLooper() {
//初始化Looper,主執行緒建立的Looper是不需要結束loop的,如果手動呼叫結束方法會丟擲異常
prepare(false);
//主執行緒的Looper只能由android系統初始化,我們自己初始化會拋異常
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
//記錄主執行緒的Looper
sMainLooper = myLooper();
}
}
/** 獲取主執行緒的Looper
*/
public static Looper getMainLooper() {
synchronized (Looper.class) {
return sMainLooper;
}
}
/**
* 返回與當前執行緒相關Looper物件,如果返回null,則說明當前執行緒沒有關聯Looper
*/
public static Looper myLooper() {
return sThreadLocal.get();
}
/**
* 返回當前執行緒繫結的Looper物件內的MessageQueue ,這個必須在Looper中執行的執行緒中呼叫,否則會報空指標異常
*/
public static MessageQueue myQueue() {
return myLooper().mQueue;
}
/**
* 輪詢MessageQueue中的Message,如果是子線則需要手動呼叫loop方法輪詢處理訊息
*/
public static void loop() {
//1.獲取當前執行緒繫結的looper物件
final Looper me = myLooper();
if (me == null) {
//在子執行緒中建立Handler,如果沒有呼叫Looper.prepare(),那麼myLooper()方法是獲取不到Looper物件的.則會拋異常,
//而主執行緒由於系統已經呼叫了prepareMainLooper方法,因此沒有這個問題
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
//2.獲取Looper的訊息佇列
final MessageQueue queue = me.mQueue;
// ...
for (;;) {
//3.這裡是死迴圈,從MessageQueue中遍歷所有的Message物件
Message msg = queue.next(); // might block (next方法是阻塞的方法)
//訊息為null,結束loop
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// ...
/* 4.獲取Message所關聯的target,這裡的target其實就是Handler物件,後面會解釋
呼叫關聯的Handler物件的dispatchMessage方法,將訊息傳遞給Handler去處理*/
msg.target.dispatchMessage(msg);
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
// ...
// 釋放訊息,清空Message中儲存的各種引用
msg.recycle();
}
}
/**
* 判斷當前Looper內的執行緒是否就是當前執行緒
*/
public boolean isCurrentThread() {
return Thread.currentThread() == mThread;
}
/**
* 非安全的退出Looper,會導致loop方法終止,退出時並不保證訊息佇列中的所有message都已處理完畢
*/
public void quit() {
mQueue.quit(false);
}
/**
* 安全的結束Looper,loop方法僅當訊息佇列中的所有message都處理完畢後才終止,但是不包括延時處理的message
*/
public void quitSafely() {
mQueue.quit(true);
}
/**
* 獲取與之Looper關聯的執行緒物件
*/
public Thread getThread() {
return mThread;
}
...
通過分析Looper的原始碼,我們可以瞭解到Looper類是用來輪詢當前執行緒相關聯的Message物件的,每輪詢一個message,就會通過該message持有的Handler的引用也就是target去處理訊息,具體是通過dispatchMessage方法分發處理訊息,稍後會講到.
預設建立的執行緒是沒有與任何Looper進行關聯的,必須要先通過Looper的prepare()靜態方法來建立並通過ThreadLocal進行關聯繫結,接著輪詢處理訊息的時候必須要手動呼叫loop方法進行處理.
子執行緒建立Handler
下面是一個經典的子執行緒中建立Handelr,並通過Looper輪詢和處理訊息的例子:
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
//初始化Looper
Looper.prepare();
//建立處理訊息的Handler,最開始已經分析過子執行緒中建立Handler,必須要先有Looper
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
//開始輪詢
Looper.loop();
}
}
在主執行緒中建立Handler的,系統已經幫我們完成了Looper.prepare();和 Looper.loop();的工作了,因此我們可以直接使用Handler.
疑問
分析Looper原始碼的時候有以下2個疑問待解決,定位到loop方法中的msg.target.dispatchMessage(msg);
1.target是何時與Message進行關聯的呢?
2.dispatchMessage是如何處理輪詢出來的訊息(message物件)?
target的由來
target是Message物件的一個屬性,要知道它是啥時候賦值的,那麼我們可以先Message的建立說起,Message有多中方式建立,通過空參的構造方法建立,或者通過其多個靜態的obtain方法建立.
空參的構造方法內一行程式碼也沒有,看不到有啥有用的線索,obtain方法倒是有幾個可以直接看出對target的賦值宣告,例如:
...
public static Message obtain(Handler h) {
Message m = obtain();
m.target = h;
return m;
}
public static Message obtain(Handler h, Runnable callback) {
Message m = obtain();
m.target = h;
m.callback = callback;
return m;
}
...
但是這些靜態方法並不是建立Message的唯一途徑,因此都不能保證target就已經關聯了Handler物件,那麼我們只能從Handler的sendMessage方法入手了.
public final boolean sendMessage(Message msg) {
//繼續往下呼叫sendMessageDelayed方法,該方法是一個延時傳送訊息的方法,這裡傳遞0表示馬上傳送.
return sendMessageDelayed(msg, 0);
}
接著sendMessageDelayed方法
public final boolean sendMessageDelayed(Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
//繼續往下呼叫sendMessageAtTime,功能是一樣的,名字不同而已.
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
接著sendMessageAtTime方法
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
//判斷此時的Looper的MessageQueue是否為null,是者列印警告log
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
//繼續往下呼叫enqueueMessage
return enqueueMessage(queue, msg, uptimeMillis);
}
接著enqueueMessage方法
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
/*ok,終於找到看到真相了.this就是當前建立的Handler,也就是說我們通過Handler的sendMessage傳送Message的時候,
該Message物件將該Handler儲存到它的target變數中.*/
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
//這一步是將訊息存放到訊息佇列中
return queue.enqueueMessage(msg, uptimeMillis);
}
Handler#dispatchMessage的處理
dispatchMessage是Handler的方法,就是用來分發處理訊息的,直接看原始碼
/**
* 分發訊息
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
//交由Message處理,通過Handler的post方法傳送的訊息Runnable會賦值給msg的callback屬性
handleCallback(msg);
} else {
//交由handler的Callback處理
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
//如果Callback的handleMessage方法返回true,那麼dispatchMessage方法將結束,意味著下面的Handler的handleMessage方法將不被回撥.
return;
}
}
/*如果mCallback==null,或者Callback的handleMessage方法返回false的話,Handler的
handleMessage才可以接受到訊息.通過Handler的無參構造方法建立Handler的話mCallback就是等於null的.
因此我們重寫Handler的handleMessage方法就可以接收到我們傳送的Message物件*/
handleMessage(msg);
}
}
//回撥給我們處理自己的訊息
public void handleMessage(Message msg) {
}
//直接處理Message內的Runnable中的run方法
private static void handleCallback(Message message) {
message.callback.run();
}
分析Handler的事件分發機制可知,當Handler收到訊息開分發的時候優先會判斷Message中是否有繫結Runnable物件,如果有的話,直接執行run方法的邏輯,否則繼續判斷Handler中是否有繫結Callback物件,如果有則執行Callback的handleMessage方法,並判斷其返回值,如果返回true則分發結束,返回false,則會執行我們重寫Handler的handleMessage方法.
總結
通過上面的Handler,Looper,Message的原始碼分析,大概的結論是:
1.Handler通過傳送和處理Message
2. Looper通過輪詢MessageQueue無限迴圈輪詢Message,同時將Message回傳給對應的Handler處理
3. 建立Handler必須要呼叫Looper.prepare()方法去初始化Looper,否則將報異常,主執行緒的Looper是由android系統去呼叫Looper.prepare()的;
4. Looper的loop()方法將是開啟訊息輪詢的關鍵方法,非UI執行緒必須
擴充套件
Handler#post方法分析
post方法會將Runnable物件新增到message queue中,Runnable中的run方法最終是在什麼執行緒中執行是要看建立該Handler所在的執行緒,如果是在主執行緒中建立的Handler,那麼該run方法就會在主執行緒中執行,否則是在子執行緒中執行.
返回值表示該Runnable是否成功的新增到了訊息佇列中
public final boolean post(Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}
getPostMessage做了什麼操作呢?
很簡單,就是建立一個Message,然後將Runnable物件賦值給它的callback 屬性.
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
接著就和Handler#sendMessage方法一樣了.將訊息傳送到Looper中的MessageQueue中.具體是通過上面提到的enqueueMessage方法.
主執行緒的Looper在哪裡建立
Activity的啟動一般會呼叫到ActivityThread,裡面有main方法,是初始化activity必經階段,主執行緒的Looper建立和呼叫輪詢都是在ActivityThread的main方法中執行的
public static void main(String[] args) {
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());
Security.addProvider(new AndroidKeyStoreProvider());
Process.setArgV0("<pre-initialized>");
//1.建立主執行緒的Looper
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
//2.建立主執行緒的Handler
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
AsyncTask.init();
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
//3.開啟輪詢
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
主執行緒中的Looper.loop()一直無限迴圈為什麼不會造成ANR?
分析Looper的loop原始碼的時候可以知道MessageQueue是通過next()方法去獲取Message的,而next()方法又是一個阻塞的方法,所以主執行緒是阻塞的,因此CPU並不會消耗太多資源在主執行緒中,像我們平時自己new 的執行緒一樣,如果run方法裡面沒有阻塞的語句,那麼很快就得結束回收掉了.
既然主執行緒阻塞了,那麼為什麼還能呼叫各種生命週期呢?
呼叫生命週期是因為有Looper,有MessageQueue,還有溝通的橋樑Handler,通過IPC機制呼叫Handler傳送各種訊息,儲存到MessageQueue中,然後在主執行緒中的Looper提取了訊息,並在主執行緒中呼叫Handler的方法去處理訊息.最終完成各種生命週期方法的呼叫.
下面貼出主執行緒的Handler的handleMessage方法的處理程式碼:
public void handleMessage(Message msg) {
if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
switch (msg.what) {
case LAUNCH_ACTIVITY: {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
ActivityClientRecord r = (ActivityClientRecord)msg.obj;
r.packageInfo = getPackageInfoNoCheck(
r.activityInfo.applicationInfo, r.compatInfo);
handleLaunchActivity(r, null);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
} break;
case RELAUNCH_ACTIVITY: {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityRestart");
ActivityClientRecord r = (ActivityClientRecord)msg.obj;
handleRelaunchActivity(r);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
} break;
case PAUSE_ACTIVITY:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
handlePauseActivity((IBinder)msg.obj, false, msg.arg1 != 0, msg.arg2);
maybeSnapshot();
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
case PAUSE_ACTIVITY_FINISHING:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
handlePauseActivity((IBinder)msg.obj, true, msg.arg1 != 0, msg.arg2);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
case STOP_ACTIVITY_SHOW:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStop");
handleStopActivity((IBinder)msg.obj, true, msg.arg2);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
case STOP_ACTIVITY_HIDE:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStop");
handleStopActivity((IBinder)msg.obj, false, msg.arg2);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
case SHOW_WINDOW:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityShowWindow");
handleWindowVisibility((IBinder)msg.obj, true);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
case HIDE_WINDOW:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityHideWindow")