本文我們主要是要介紹Handler機(jī)制,但是涉及到Handler又不得不介紹Message,MessageQueue,Looper,Handler機(jī)制主要是依賴后面幾個(gè)的,所以我們?cè)谖闹袝?huì)一次介紹到他們幾個(gè)。
通過本文你可能會(huì)了解到一下幾點(diǎn)
- 1.Handler機(jī)制及Handler與Message,MessageQueue,Looper的關(guān)系
- 2.Handler在子線程中的應(yīng)用及原理
- 3.Message的復(fù)用機(jī)制
- 4.為什么主線程可以有死循環(huán)(loop())
Handler源碼位置:
android\frameworks\base\core\java\android\os\Handler.java
首先看他的構(gòu)造:
public Handler() {
this(null, false);
}
public Handler(Callback callback) {
this(callback, false);
}
public Handler(Looper looper) {
this(looper, null, false);
}
public Handler(Looper looper, Callback callback) {
this(looper, callback, false);
}
public Handler(boolean async) {
this(null, async);
}
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) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
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;
}
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
一般我們都用的是無參的構(gòu)造。先說一下那個(gè)Callback,他是一個(gè)接口:
public interface Callback {
public boolean handleMessage(Message msg);
}
這是處理消息的一種手段,一般我們也很少用,因?yàn)槲覀兌贾貙懥薶andleMessage方法,效果一樣的,我們主要從無參構(gòu)造這條線看下去。
在無參構(gòu)造中,直接調(diào)用了參數(shù)類型為Callback 和boolean 的構(gòu)造。這里面他給Looper賦值為Looper.myLooper()。來看一下這個(gè)方法:
Looper源碼位置:
android\frameworks\base\core\java\android\os\Looper.java
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
這個(gè)方法實(shí)際上就是獲取當(dāng)前線程的looper,ThreadLocal是Java的lang包中的類,他的set方法是在當(dāng)前線程中保存一個(gè)對(duì)象,get是從當(dāng)前線程中取出那個(gè)對(duì)象。這里存取的就是Looper。但這個(gè)Looper在哪存的呢?還是從源碼中找答案:
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");
}
sThreadLocal.set(new Looper(quitAllowed));
}
有些人看到這應(yīng)該就明白了,這里也給出了另一個(gè)問題的答案,為什么在子線程中使用Handler或者Toast時(shí)要先調(diào)用Looper.prepare()。原來這個(gè)方法有一個(gè)重要的作用就是給當(dāng)前線程設(shè)置Looper,如果不設(shè)置,就是null,當(dāng)然會(huì)有問題。但為什么主線程中不需要呢?
這個(gè)問題要從app的創(chuàng)建說起,但具體的app啟動(dòng)流程這里就不詳細(xì)敘述了,只說明一點(diǎn),在啟動(dòng)app的進(jìn)程時(shí),Zygote會(huì)通過socket接受AMS的請(qǐng)求,通過反射的方法調(diào)用ActivityThread的main方法,ActivityThread也就是一個(gè)應(yīng)用的線程,也就是主線程,我們看一下這個(gè)方法:
public static void main(String[] args) {
....
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
....
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
這里調(diào)用了prepareMainLooper方法,它和prepare有什么區(qū)別呢?直接看源碼:
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
可見prepareMainLooper也是調(diào)用了prepare,只有參數(shù)的差別,一個(gè)為false一個(gè)為true。這個(gè)參數(shù)最后用到了Looper的構(gòu)造上:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
在構(gòu)造上又傳給了MessageQueue。這個(gè)參數(shù)quitAllowed的作用在MessageQueue中很清楚:
android\frameworks\base\core\java\android\os\MessageQueue.java
void quit(boolean safe) {
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
....
}
只在這里用到了上文中的參數(shù)quitAllowed,也就是為true時(shí),會(huì)報(bào)異常,異常也很清楚,就是主線程不允許調(diào)用quit退出,這個(gè)值只在調(diào)用prepareMainLooper方法時(shí)為true,我們用的時(shí)候都是調(diào)用prepare方法,也就是為false。
這里做一個(gè)小結(jié),Looper.prepare()最主要的作用是初始化Looper,而Handler是基于Looper,一個(gè)線程創(chuàng)建后是沒有Looper,主線程也不例外,要想使用Handler就必須初始化,一般線程調(diào)用prepare方法,主線程調(diào)用prepareMainLooper。
下面我們還回到構(gòu)造方法中去。
這里判斷了一下mLooper 是否為空,若為空拋異常,這也就是子線程中不能直接使用的直接來源。然后初始化了MessageQueue類型成員變量mQueue ,他從Looper中獲得,Looper中是在構(gòu)造函數(shù)中初始化的。又初始化了成員變量mCallback ,當(dāng)然我們這條線上他為空。最后初始化了標(biāo)志位mAsynchronous ,我們這里為false,這個(gè)標(biāo)志位的左后后面會(huì)提到。
到這里Handler的構(gòu)造就完成了,除了初始化一些東西也沒做什么。我們一般用的時(shí)候在構(gòu)造完之后,就是重寫handleMessage方法,用于處理接受到的消息。下面分析從發(fā)送消息到接受消息的流程。
先從最普通的sendMessage開始:
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);
}
發(fā)現(xiàn)sendMessage內(nèi)部調(diào)用的是sendMessageDelayed,也就是延遲為0,很好理解。主要看sendMessageAtTime
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);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
這個(gè)里面先獲取了消息隊(duì)列,這個(gè)變量在構(gòu)造中初始化,就是Looper中的隊(duì)列,在looper中的構(gòu)造中初始化。接下來調(diào)用了enqueueMessage方法,這個(gè)方法里,首先設(shè)置Message 的target 為當(dāng)前Handler,方便調(diào)用sendToTarget時(shí)使用。最后調(diào)用了MessageQueue 的enqueueMessage方法入隊(duì)。我們看一下這個(gè)方法,還是很有意思的:
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) {
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
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;
prev.next = msg;
}
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
這里就需要有一些數(shù)據(jù)結(jié)構(gòu)的功底了。這個(gè)方法的主要思想是利用next 成員創(chuàng)建一個(gè)鏈表。方法中首先判斷了target 是否為空及此msg是否使用,然后判斷了mQuitting變量,這個(gè)變量在quit方法中為true,在主線程中這個(gè)變量永遠(yuǎn)為false。接下來標(biāo)記該msg已用,然后配置該msg的執(zhí)行時(shí)間。
下面就是鏈表的操作了,如果鏈表為空,或者該msg需要立即執(zhí)行或者,該msg執(zhí)行時(shí)間小于第一位的msg,就把他插入到鏈表頭部(鏈表插入方法中的頭插法,代碼看不懂的可以畫圖手動(dòng)操作一些,會(huì)有奇效)。
如果不滿足上面三個(gè)條件,就循環(huán)遍歷所有鏈表結(jié)點(diǎn),根據(jù)時(shí)間插入到合適位置,這里涉及到鏈表的插入操作,代碼看不懂的畫圖演示比較直觀有效。其中有一點(diǎn)很重要,這個(gè)鏈表是有序的。
消息進(jìn)入隊(duì)列后,發(fā)消息的第一步就是要先從隊(duì)列中取消息,我們來看這一步,這一步在Looper中的loop方法,還記得如何在子線程中使用Handler么,執(zhí)行完prepare方法后,就可以設(shè)置Handler了,但此時(shí)Handler還沒有用,缺少一個(gè)驅(qū)動(dòng),這個(gè)驅(qū)動(dòng)就是Looper.loop()。看一下這個(gè)方法:
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;
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
for (;;) {
Message msg = queue.next();
if (msg == null) {
return;
}
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
final long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
final long traceTag = me.mTraceTag;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
final long end;
try {
msg.target.dispatchMessage(msg);
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
if (slowDispatchThresholdMs > 0) {
final long time = end - start;
if (time > slowDispatchThresholdMs) {
Slog.w(TAG, "Dispatch took " + time + "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);
}
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();
}
}
這里首先也驗(yàn)證了一下,保證Looper 不為空。然后獲取了MessageQueue 。接下來開啟了一個(gè)死循環(huán)(看到這,有人也許會(huì)問,在ActivityThread中也調(diào)用了該方法,為什么主線程中有個(gè)死循環(huán),而且還沒問題,這個(gè)問題最后在講),這個(gè)循環(huán)的目的就是不斷地從隊(duì)列中取消息,取消息用的是queue.next(),這是一個(gè)阻塞方法(這個(gè)過程類似于Socket中服務(wù)端監(jiān)聽請(qǐng)求的過程),我們來看next方法:
Message next() {
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1;
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
nextPollTimeoutMillis = -1;
}
if (mQuitting) {
dispose();
return null;
}
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler
boolean keep = false;
try {
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
pendingIdleHandlerCount = 0;
nextPollTimeoutMillis = 0;
}
}
先看第一個(gè)判斷,這里出現(xiàn)一個(gè)變量mPtr,這個(gè)變量是什么哪里來的?他是在構(gòu)造中初始化的,調(diào)用了native方法nativeInit();既然是源碼分析,所以雖然是jni我們也要去看看。源碼位置:
android\frameworks\base\core\jni\android_os_MessageQueue.cpp
static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
if (!nativeMessageQueue) {
jniThrowRuntimeException(env, "Unable to allocate native queue");
return 0;
}
nativeMessageQueue->incStrong(env);
return reinterpret_cast<jlong>(nativeMessageQueue);
}
這里創(chuàng)建了native層的MessageQueue對(duì)象,這個(gè)類定義就在android_os_MessageQueue.cpp中。nativeInit的返回值只是為了標(biāo)志NativeMessageQueue是否創(chuàng)建成功,不成功的會(huì)返回0,成功的返回指針地址(轉(zhuǎn)為long類型),另外在MessageQueue 的dispose()方法中會(huì)把他置為0。我們主要還是回到next方法中,這個(gè)標(biāo)志位主要標(biāo)志該隊(duì)列是否可用,不可用返回null即可。下面才是正式的取消息部分。
這里你會(huì)驚奇的發(fā)現(xiàn)有一個(gè)死循環(huán),這也就是為什么這個(gè)方法時(shí)阻塞的原因??磥淼搅溯^底層的部分,也沒有什么花哨技巧了,直接用上死循環(huán)來處理。我們直接看synchronized 代碼塊的第一個(gè)判斷,里面有一個(gè)target == null的判斷,我們?cè)趀nqueueMessage知道,如果target 為null的話會(huì)直接拋異常,那么會(huì)什么會(huì)有target 為空的msg呢?實(shí)際上MessageQueue 有這樣一個(gè)方法:
public int postSyncBarrier() {
return postSyncBarrier(SystemClock.uptimeMillis());
}
private int postSyncBarrier(long when) {
synchronized (this) {
final int token = mNextBarrierToken++;
final Message msg = Message.obtain();
msg.markInUse();
msg.when = when;
msg.arg1 = token;
Message prev = null;
Message p = mMessages;
if (when != 0) {
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
}
if (prev != null) {
msg.next = p;
prev.next = msg;
} else {
msg.next = p;
mMessages = msg;
}
return token;
}
}
這個(gè)方法也是插入一個(gè)msg,但是是target為空的特殊msg,稱之為SyncBarrier。這種msg的作用就是攔截,所有執(zhí)行時(shí)間在這個(gè)msg之后的同步消息都不會(huì)執(zhí)行,直到遇到下一個(gè)SyncBarrier,也就是異步和同步之分。如果你記憶力夠好或者沒有被源碼繞暈的話,希望你記得在Handler構(gòu)造中初始化了一個(gè)參數(shù)mAsynchronous ,若從空參來的話,他為false。在Handler的enqueueMessage中發(fā)揮作用,若為true的話,所有msg會(huì)setAsynchronous(true)。一直影響到next方法里,若isAsynchronous()為true,會(huì)不受異步影響,若isAsynchronous()為false,會(huì)受異步影響,跳過所有同步消息。好吧,跨度有點(diǎn)大,多回頭看看源碼捋一捋就好。
我們從正常情況開始分析,就是isAsynchronous()為false,但是沒有插入SyncBarrier。這時(shí)取到頭部msg,判斷是否為null,不為空的話,檢測執(zhí)行時(shí)間,如果未到時(shí)間,則設(shè)置nextPollTimeoutMillis 為等待時(shí)間,若到時(shí)間,則從隊(duì)列移除該msg,重設(shè)頭結(jié)點(diǎn),并返回該消息。這時(shí)正常取到msg的流程,若msg==null,設(shè)置nextPollTimeoutMillis =-1,然后判斷是不是因?yàn)檎{(diào)用quit導(dǎo)致的(調(diào)用該方法會(huì)清空隊(duì)列),若是則返回null。最后看一下是否有IdleHandlers,IdleHandler是是一種利用系統(tǒng)空閑時(shí)機(jī)去處理一些不重要事件用的,如gc,這和我們要講的消息關(guān)系不大。且看邏輯,如果沒有要執(zhí)行的IdleHandlers,則阻塞設(shè)為true,繼續(xù)循環(huán)(此時(shí)意味著取到了一個(gè)取到了一個(gè)消息,但還沒到執(zhí)行事件,nextPollTimeoutMillis為大于零某數(shù),或沒有取到消息,nextPollTimeoutMillis為-1),下一次循環(huán),會(huì)到nativePollOnce方法,我們這里介紹一下這個(gè)方法:
static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,
jlong ptr, jint timeoutMillis) {
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
nativeMessageQueue->pollOnce(env, obj, timeoutMillis);
}
void NativeMessageQueue::pollOnce(JNIEnv* env, jobject pollObj, int timeoutMillis) {
mPollEnv = env;
mPollObj = pollObj;
mLooper->pollOnce(timeoutMillis);
mPollObj = NULL;
mPollEnv = NULL;
if (mExceptionObj) {
env->Throw(mExceptionObj);
env->DeleteLocalRef(mExceptionObj);
mExceptionObj = NULL;
}
}
他先還原了之前保存的NativeMessageQueue對(duì)象,然后執(zhí)行了mLooper->pollOnce(timeoutMillis)。mLooper是native層的Looper對(duì)象,在NativeMessageQueue構(gòu)造中初始化,他和java的Looper并沒多大關(guān)系:
NativeMessageQueue::NativeMessageQueue() :
mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) {
mLooper = Looper::getForThread();
if (mLooper == NULL) {
mLooper = new Looper(false);
Looper::setForThread(mLooper);
}
}
Looper.cpp位置:
android\system\core\libutils\Looper.cpp
pollOnce內(nèi)部又調(diào)用了pollInner方法,這個(gè)方法非常長,這里就不貼出來了,關(guān)鍵一步在:
int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
到這里已經(jīng)超出Android編程范圍了,涉及到了Linux編程,這里涉及到了epoll機(jī)制,這里可以這樣理解,在java層,next方法會(huì)阻塞,阻塞的實(shí)質(zhì)就是這里,java層調(diào)用的時(shí)nativePollOnce方法,next初始時(shí)nextPollTimeoutMillis為0,立即返回,不阻塞,隨后取一個(gè)msg,計(jì)算等待時(shí)間,下次循環(huán)調(diào)用nativePollOnce時(shí)開始阻塞。(簡單介紹一下,需要詳細(xì)了解的朋友還是要翻看代碼)。
這里面還有一點(diǎn),在沒取到消息時(shí),nextPollTimeoutMillis=-1,可能會(huì)永久阻塞,但是在MessageQueue 的enqueueMessage方法中,當(dāng)成功添加后,有一個(gè)判斷:
if (needWake) {
nativeWake(mPtr);
}
這個(gè)needWake在添加隊(duì)列中第一個(gè)消息時(shí),被賦值,就是mBlocked的值,mBlocked 在next()中需要循環(huán)時(shí)被置為true。nativeWake就是去喚醒,不再阻塞。起始只有隊(duì)列為空時(shí)再回取到null消息,所以在添加第一個(gè)消息到隊(duì)列時(shí),自然要解除阻塞??梢钥匆幌耼ativeWake:
static void android_os_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jlong ptr) {
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
nativeMessageQueue->wake();
}
void NativeMessageQueue::wake() {
mLooper->wake();
}
void Looper::wake() {
#if DEBUG_POLL_AND_WAKE
ALOGD("%p ~ wake", this);
#endif
uint64_t inc = 1;
ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd, &inc, sizeof(uint64_t)));
if (nWrite != sizeof(uint64_t)) {
if (errno != EAGAIN) {
LOG_ALWAYS_FATAL("Could not write wake signal to fd %d: %s",
mWakeEventFd, strerror(errno));
}
}
}
在Looper::rebuildEpollLocked方法中
eventItem.data.fd = mWakeEventFd;
int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeEventFd, & eventItem);
wake中執(zhí)行了write(mWakeEventFd, &inc, sizeof(uint64_t)),mWakeEventFd就是epoll監(jiān)聽的事件,通過wake()就解除了阻塞。
分析了這么長,又摻入native部分,總算把取消息的流程說完了,這一部分要多看幾遍源碼,仔細(xì)琢磨。下面我們回到loop方法中。
當(dāng)取到一個(gè)消息,并不為空時(shí),調(diào)用了msg.target.dispatchMessage(msg)去分發(fā)事件:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
到這里就回調(diào)到了handleMessage,進(jìn)行了一次選擇,如果實(shí)現(xiàn)了callback 就走callback ,否則走h(yuǎn)andleMessage方法。到這里整個(gè)發(fā)送接收體系就完全介紹完了。
在loop方法最后,調(diào)用了msg.recycleUnchecked();
void recycleUnchecked() {
// Mark the message as in use while it remains in the recycled object pool.
// Clear out all other details.
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = -1;
when = 0;
target = null;
callback = null;
data = null;
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
這個(gè)方法是在一個(gè)Message用完之后調(diào)用的,清空了Message的所有內(nèi)容,并且加入了一個(gè)Pool,看著像一個(gè)池子,實(shí)際上是Message利用next成員建立起的一個(gè)鏈表,為什么要用鏈表,且看obtain方法:
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
這是一個(gè)靜態(tài)方法,可以代替new Message()獲得一個(gè)Message實(shí)例。方法中,就是取到了鏈表的頭部那個(gè)Message,這個(gè)鏈表在每次loop循環(huán)后增長,形成緩存池,實(shí)現(xiàn)了Message的復(fù)用,節(jié)省了內(nèi)存。這個(gè)鏈表最大長度為50(在Android 8.0中),所以日常開發(fā)時(shí),我們應(yīng)該盡可能的調(diào)用obtain方法去獲得一個(gè)Message,實(shí)際上源碼也是這樣做的。
public final boolean sendEmptyMessage(int what)
{
return sendEmptyMessageDelayed(what, 0);
}
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}
除了讓Looper控制建立緩存,我們也可以自己回收,雖然我們不能調(diào)用recycleUnchecked方法(有訪問權(quán)限控制),但是我們可以調(diào)用recycle方法,回收一個(gè)Message,他只是做一個(gè)安全檢查而已。
public void recycle() {
if (isInUse()) {
if (gCheckRecycle) {
throw new IllegalStateException("This message cannot be recycled because it "
+ "is still in use.");
}
return;
}
recycleUnchecked();
}
最后,我們來解釋文中的一個(gè)遺留問題,為什么ActivityThread這個(gè)應(yīng)用主線程可以有一個(gè)死循環(huán)。
首先看,如果沒有死循環(huán),Looper.loop()執(zhí)行完之后,直觀來看會(huì)拋一個(gè)異常
new RuntimeException("Main thread loop unexpectedly exited");
實(shí)際上,就算不拋異常,那么到這這個(gè)main方法就執(zhí)行完了,你站在java代碼角度看,main就執(zhí)行完,程序不就結(jié)束了么?
有過socket開發(fā)經(jīng)驗(yàn)的朋友,肯定不會(huì)讓服務(wù)端接受一個(gè)請(qǐng)求后就讓main函數(shù)結(jié)束,一般會(huì)一直循環(huán)監(jiān)聽請(qǐng)求。
還有另外一個(gè)更生動(dòng)的場景,我們?cè)趯W(xué)習(xí)c語言時(shí),第一個(gè)程序都是helloworld,有些編譯器很智能,打印完helloworld,程序暫停,但有些直接就退出,現(xiàn)象就是我們執(zhí)行程序時(shí),屏幕閃一下黑框就什么都沒了,大多數(shù)人都會(huì)一臉懵逼的去百度,然后加個(gè)暫?;蛘叩却斎氲暮瘮?shù),讓控制臺(tái)保留下來,得意的看著自己第一個(gè)程序。但實(shí)際上程序執(zhí)行完打印語句后就應(yīng)該退出了,我們只是讓程序阻塞住而已。
android也一樣,所以要有一個(gè)死循環(huán),保證主線程不自動(dòng)結(jié)束。但為什么要死循環(huán)呢?注意一點(diǎn),loop()方法的循環(huán)并不是無意義的循環(huán),而是不斷取事件執(zhí)行事件,Android是建立在事件驅(qū)動(dòng)機(jī)制上的,程序在創(chuàng)建運(yùn)行過程中有很多事件,都是Handler處理的,所以要有一個(gè)loop()去驅(qū)動(dòng)事件執(zhí)行。我們大致可以看下ActivityThread中的Handler方法:
定義了非常多事件,例如第一個(gè)LAUNCH_ACTIVITY事件中,就創(chuàng)建了Activity實(shí)例。
至于為什么不會(huì)ANR,其實(shí)ANR并不是一種錯(cuò)誤,只是系統(tǒng)認(rèn)為我們?cè)谀承┑胤胶臅r(shí)太長,造成了流程上的阻塞,是一種檢測機(jī)制。從上文我們可以知道,Handler是順序執(zhí)行事件的,一個(gè)事件執(zhí)行的時(shí)間過長,就導(dǎo)致后續(xù)事件阻塞,所以必須有ANR機(jī)制去檢測。換句話說,如果沒有ANR檢測,其實(shí)也是沒問題的,只不過會(huì)導(dǎo)致界面一直卡著,或者我們執(zhí)行某個(gè)操作長時(shí)間無反應(yīng)而已,正如出現(xiàn)無響應(yīng)時(shí)系統(tǒng)會(huì)給我們一個(gè)繼續(xù)等待的選項(xiàng)。而這里的死循環(huán),相當(dāng)于后臺(tái),事件相當(dāng)于前臺(tái),后臺(tái)阻塞的去監(jiān)聽前臺(tái)請(qǐng)求是沒問題的。