Android的View模型采用的是單線程模型,所有的視圖相關(guān)的操作都必須在主線程中進(jìn)行,否則會拋出異常。某些耗時的操作需要放入工作線程中,執(zhí)行完后通過異步消息處理機(jī)制把結(jié)果傳入主線程中進(jìn)行刷新UI等操作。異步消息處理機(jī)制主要由Handler\Looper\MessageQueue\Message幾個類,它工作的原理網(wǎng)上有很多資料,這里不再贅述。接下來深入剖析下相關(guān)源碼(源碼基于android8.0)。
1. Message
Message顧名思義,是一個消息,是多線程間通信的實(shí)體,是Handler發(fā)送和處理的對象。Message對象實(shí)現(xiàn)了Parcelable接口,說明Message對象支持序列化/反序列化操作。
成員變量
//標(biāo)識消息的code
public int what;
//存儲int類型的數(shù)據(jù)域
public int arg1;
//存儲int類型的數(shù)據(jù)域
public int arg2;
//存儲對象的數(shù)據(jù)域
public Object obj;
//消息標(biāo)識,當(dāng)消息被回收放入到消息池時會被打上FLAG_IN_USE標(biāo)識
/*package*/
int flags;
//記錄消息的時間戳
/*package*/
long when;
//Bundle數(shù)據(jù)域
/*package*/
Bundle data;
//發(fā)送和處理消息的Handler
/*package*/
Handler target;
//回調(diào)接口,消息可以被Handler處理,也可以被自身的Runnable對象處理
/*package*/
Runnable callback;
// 鏈?zhǔn)浇Y(jié)構(gòu),指向下一個Massgage對象,用于維護(hù)鏈表結(jié)構(gòu)的消息池
/*package*/
Message next;
//靜態(tài)域,信號量,可理解成這個對象是為了同步加鎖創(chuàng)建的,所有需要對消息池進(jìn)行的操作,會對該對象進(jìn)行加鎖同步
private static final Object sPoolSync = new Object();
//靜態(tài)域,鏈表結(jié)構(gòu)消息池的表頭,由它維護(hù)了一個鏈?zhǔn)娇臻e消息池,當(dāng)消息被回收的時候,會加入到這個消息池中
private static Message sPool;
//靜態(tài)域,消息池大小
private static int sPoolSize = 0;
//消息池最大容量是50
private static final int MAX_POOL_SIZE = 50;
//在回收消息之前是否需要檢查消息是否是在被使用當(dāng)中
private static boolean gCheckRecycle = true;
從Message定義的這些變量可知以下幾點(diǎn):
- Message中分別定義了幾個數(shù)據(jù)域,可以用來存儲數(shù)據(jù)(arg1,arg2,obj,data)。
- Message中維護(hù)了一個全局的消息池,消息池的最大容量是50
obtain()方法
通過Meaasge.obtain()可以獲得一個Message對象:
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();
}
獲取一個Message對象的源碼很簡單,先加鎖,如果消息池是空的,直接new一個新的Message對象出來,否則從鏈表頭取出一個Message對象,把它暫時從消息池中刪除,并清除它的flag。需要注意的是,obtain靜態(tài)方法有很多重載的實(shí)現(xiàn),但都是先調(diào)用Meaasge.obtain()方法獲取到一個Message對象,再對相關(guān)變量進(jìn)行賦值。
recycle()方法
在一個Message被使用完以后,調(diào)用recycle()方法會被回收:
public void recycle() {
if (isInUse()) {
if (gCheckRecycle) {
throw new IllegalStateException("This message cannot be recycled because it " + "is still in use.");
}
return;
}
recycleUnchecked();
}
/**
* Recycles a Message that may be in-use.
* Used internally by the MessageQueue and Looper when disposing of queued Messages.
*/
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++;
}
}
}
recycle()方法其實(shí)調(diào)用的是recycleUnchecked()方法,在這個方法中,會將flag標(biāo)記為FLAG_IN_USE,并把所有的域清空,并在消息池沒有達(dá)到最大限定值的情況下,把這個對象插入消息池的表頭。同樣,在操作消息池的時候需要先對sPoolSync信號量加鎖。
Message除了上述關(guān)鍵的方法以外,還有一些setter/getter、toString()方法、設(shè)置消息flag、判斷消息flag和實(shí)現(xiàn)Parceable接口的方法,具體不再分析,感興趣的同學(xué)可以自行去查看。
2.MessageQueue
MessageQueue是消息處理隊(duì)列,在異步消息處理中,需要調(diào)用enqueueMessage()方法將消息入隊(duì)到MessageQueue中,然后通過next()方法取出,交給Handler去處理消息。MessageQueue的類由final修飾,禁止對其進(jìn)行繼承、修改。
成員變量
下面來看MessageQueue中定義的成員變量變量:
// True if the message queue can be quit.
private final boolean mQuitAllowed;
@SuppressWarnings("unused") private long mPtr; // used by native code
Message mMessages;
private final ArrayList < IdleHandler > mIdleHandlers = new ArrayList < IdleHandler > ();
private SparseArray < FileDescriptorRecord > mFileDescriptorRecords;
private IdleHandler[] mPendingIdleHandlers;
private boolean mQuitting;
// Indicates whether next() is blocked waiting in pollOnce() with a non-zero timeout.
private boolean mBlocked;
// The next barrier token.
// Barriers are indicated by messages with a null target whose arg1 field carries the token.
private int mNextBarrierToken;
- mQuitAllowed: 是否允許退出,主線程的MessageQueue在初始化的時候賦值為true,在退出的時候會對其進(jìn)行判斷,如果不允許退出,則拋出異常。
- mPtr:native層使用的code,暫不做分析;
- mMessage: 消息隊(duì)列鏈表的表頭,存儲入隊(duì)的消息,取消息的時候也是從它里邊取消息;
- mIdleHandlers :存放IdleHandler的AarrayList,在消息隊(duì)列中沒有消息或者要處理的消息是在未來才被執(zhí)行時,會去遍歷執(zhí)行mIdleHandlers中的queueIdle()方法;
- mPendingIdleHandlers:mIdleHandlers中的IdleHandler要執(zhí)行時,會先存放在mPendingIdleHandler中,真正得到執(zhí)行的其實(shí)是這個數(shù)組中的IdleHandler;
- mQuitting:標(biāo)識MessageQueue是否在執(zhí)行退出,消息隊(duì)列在退出狀態(tài)時不可以再入隊(duì)消息,否則會報(bào)出異常
IdleHandler
public static interface IdleHandler {
boolean queueIdle();
}
public void addIdleHandler(@NonNull IdleHandler handler) {
if (handler == null) {
throw new NullPointerException("Can't add a null IdleHandler");
}
synchronized(this) {
mIdleHandlers.add(handler);
}
}
public void removeIdleHandler(@NonNull IdleHandler handler) {
synchronized(this) {
mIdleHandlers.remove(handler);
}
}
從上述部分源碼知道,IdleHandler是一個接口,可以通過addIdleHandler和removeHandler往mIdleHandlers中添加和移除IdleHandler的實(shí)現(xiàn)類的實(shí)例。
構(gòu)造方法
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}
MessageQueue的構(gòu)造函數(shù)需要傳入quitAllowed參數(shù),代表是否允許MessageQueue退出。
next()方法
//取出下一個入隊(duì)的消息
Message next() {
// Return here if the message loop has already quit and been disposed.
// This can happen if the application tries to restart a looper after quit
// which is not supported.
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
//需要處理的IdleHandler的數(shù)量
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
//死循環(huán)去拿Message,直到返回一個Message,或者M(jìn)essageQueue退出
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized(this) {
// Try to retrieve the next message. Return if found.
//去拿一個消息,拿到后return
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
//拿到隊(duì)頭,如果拿到的是一個屏障
//(或者叫攔截器,target一定為null,arg1里保存著屏障的token),
//則往后遍歷隊(duì)列,直到拿到一個異步消息
Message msg = mMessages;
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while ( msg != null && ! msg . isAsynchronous ());
}
if (msg != null) {
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
//如果拿到的消息的執(zhí)行時間未到,更新nextPollTimeoutMills
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
mBlocked = false;
if (prevMsg != null) {
//preMsg不為空,說明此時隊(duì)列頭是一個barrier,msg此時是異步消息。
//preMsg是msg的前驅(qū)節(jié)點(diǎn),把msg從隊(duì)列中刪除
prevMsg.next = msg.next;
} else {
//此時Msg依舊為隊(duì)頭,所以只需要將頭結(jié)點(diǎn)刪掉即可
mMessages = msg.next;
}
//將取出的消息的next域置為null
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
//把消息msg打上FLAG_IN_USE操作,使用的是位或運(yùn)算
msg.markInUse();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
// If first time idle, then get the number of idlers to run.
// Idle handles only run if the queue is empty or if the first message
// in the queue (possibly a barrier) is due to be handled in the future.
//如果隊(duì)列為空或者隊(duì)頭的消息沒有到執(zhí)行時間(在未來執(zhí)行),
//則執(zhí)行idleHandlers的queueIdle()
if (pendingIdleHandlerCount < 0 && (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
//沒有IdleHandlers需要被執(zhí)行,則繼續(xù)for循環(huán),直到拿到消息
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
//把mIdleHanders的內(nèi)容復(fù)制到mPendingIdleHandlers的數(shù)組中,等待被執(zhí)行
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// Run the idle handlers.
// We only ever reach this code block during the first iteration.
//遍歷pendingIdleHandler數(shù)組,依次執(zhí)行IdleHandler.queueIdle()方法
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 {
//執(zhí)行queueIdle()
keep = idler.queueIdle();
} catch(Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
//如果queueIdle()的執(zhí)行結(jié)果為false,
//則把對應(yīng)的IdleHandler從mIdleHandlers中移除,
//否則,下次隊(duì)列空閑的時候,還會把mIdleHandlers的
//內(nèi)容復(fù)制到mPendingIdleHandlers中再次執(zhí)行;
if (!keep) {
synchronized(this) {
mIdleHandlers.remove(idler);
}
}
}
// Reset the idle handler count to 0 so we do not run them again.
//執(zhí)行完以后把變量置0
pendingIdleHandlerCount = 0;
// While calling an idle handler, a new message could have been delivered
// so go back and look again for a pending message without waiting.
nextPollTimeoutMillis = 0;
}
}
針對next()方法需要說明以下幾點(diǎn):
- next方法是從消息隊(duì)列中取出一個可以被執(zhí)行的消息,并將其從隊(duì)列中刪除;如果隊(duì)列為空,或者隊(duì)頭的消息還未到執(zhí)行時間(需要在未來才執(zhí)行),則會執(zhí)行mIdleHandlers中的空閑隊(duì)列IdleHandler.queueIdle()方法;
- next()方法在執(zhí)行時,如果遇到當(dāng)前隊(duì)列確實(shí)為空時,會去執(zhí)行空閑隊(duì)列,但是不會退出循環(huán),也不會返回null,直到返回一個Messsage對象,或者M(jìn)essageQueue退出,所以可以根據(jù)next()返回的null來判斷MessageQueue是否退出了;
- IdleHandler.queueIdle()方法的返回值代表是否要保留這個IdleHandler,true為保留,再下次執(zhí)行空閑隊(duì)列時會再次被執(zhí)行,除非手動remove掉這個IdleHandler;false為不保留,執(zhí)行一次則會被移除。可以利用這個來優(yōu)化頁面啟動,把頁面啟動時比較復(fù)雜的邏輯利用IdleHandler去做,這樣可以讓主線程先處理完UI相關(guān)的事件后再去處理負(fù)責(zé)的邏輯,可以減少頁面啟動白屏?xí)r間。
- Android消息處理機(jī)制中增加了Barrier機(jī)制,稱作屏障或者攔截器、監(jiān)視器,設(shè)置它的目的在于區(qū)分同步消息和異步消息。為了讓View能夠快速的布局和繪制,當(dāng)View在繪制和布局時會向Looper中添加了Barrier,這樣后續(xù)的消息隊(duì)列中的同步的消息將不會被執(zhí)行,以免會影響到UI繪制,但是只有異步消息才能被執(zhí)行。當(dāng)不設(shè)置Barrier時,消息的同步和異步?jīng)]有任何區(qū)別,都是依次按照when的時間先后被執(zhí)行,但是假如Barrier時,隊(duì)列中就只執(zhí)行異步消息,直到我們調(diào)用removeBarrier移除了這個Barrier。
- 消息默認(rèn)是同步消息(flag默認(rèn)是0,1<<1代表消息是異步消息),調(diào)用Message的setAsynchronous可以把消息變?yōu)楫惒较?,但是這個方法是Hide的,只能由Handler去調(diào)用,在構(gòu)造Handler時,需要傳入是否為異步消息的變量,具體源碼下邊分析;
- 針對隊(duì)列的操作,進(jìn)行了同步操作,在操作隊(duì)列之前,先進(jìn)行了加鎖,所以MessageQueue是線程安全的消息隊(duì)列;
enqueueMessage()方法
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
//入隊(duì)的消息的target,必須不為空,否則會拋異常
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
//消息在回收之前,不可以被重復(fù)使用
throw new IllegalStateException(msg + " This message is already in use.");
}
//加鎖同步
synchronized (this) {
if (mQuitting) {
//如果消息隊(duì)列在退出狀態(tài) ,則直接回收消息,返回false
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;
}
//把消息標(biāo)記為在使用狀態(tài),設(shè)置when
msg.markInUse();
msg.when = when;
//此時p指向鏈表頭
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
//如果隊(duì)列為空或者when等于0,或者when小于
//隊(duì)頭Message的when,則直接把消息插入隊(duì)頭
// 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的前驅(qū)節(jié)點(diǎn),依次遍歷
prev = p;
p = p.next;
//當(dāng)p已經(jīng)到隊(duì)尾或者找到一個節(jié)點(diǎn)msg.when < p.when時退出循環(huán)
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
//鏈表插入操作,把msg插入到p節(jié)點(diǎn)前邊,并把p的前驅(qū)節(jié)點(diǎn)的next改為msg
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
//插入成功,返回true
return true;
}
針對消息入隊(duì)函數(shù)enqueueMessage()方法,需要說明以下幾點(diǎn):
- 該方法對鏈表隊(duì)列操作時,依然是進(jìn)行了加鎖同步,所以是線程安全的;
- 返回值true代表入隊(duì)成功,false代表入隊(duì)失敗,在隊(duì)列已經(jīng)退出狀態(tài)時再次入隊(duì)消息會返回false;
- 隊(duì)列是根據(jù)when值(也就是所謂的消息執(zhí)行時間)大小組成的一個升序鏈表,插入也必須找到合適的節(jié)點(diǎn)進(jìn)行插入,所以next遍歷消息的時候,只需要看隊(duì)頭消息的when是否到執(zhí)行時間就可以判斷是否需要執(zhí)行mIdleHandlers里的空閑隊(duì)列;
quit()方法
void quit(boolean safe) {
if (!mQuitAllowed) {
//如果不允許退出,調(diào)用該方法,會拋出異常
throw new IllegalStateException("Main thread not allowed to quit.");
}
//對自身進(jìn)行加鎖同步
synchronized(this) {
//如果已經(jīng)是退出狀態(tài)了,再次退出,直接返回
if (mQuitting) {
return;
}
//把mQuitting置為true,標(biāo)識隊(duì)列已經(jīng)退出。一旦隊(duì)列
//是退出狀態(tài),則無法再次使用,除非再次new一個MessageQueue
mQuitting = true;
//參數(shù)safe標(biāo)識是否為安全退出,ture會將還未到執(zhí)行時間的Message
//(在未來時刻執(zhí)行)remove掉并清除Message的內(nèi)容;false會移除
//全部Message并清除Message的內(nèi)容
if (safe) {
removeAllFutureMessagesLocked();
} else {
removeAllMessagesLocked();
}
// We can assume mPtr != 0 because mQuitting was previously false.
nativeWake(mPtr);
}
}
private void removeAllFutureMessagesLocked() {
final long now = SystemClock.uptimeMillis();
Message p = mMessages;
if (p != null) {
//如果隊(duì)頭的執(zhí)行時間已經(jīng)是在未來時刻了,則直接調(diào)用
//removeAllMessagesLocked()方法清除所有的消息
if (p.when > now) {
removeAllMessagesLocked();
} else {
Message n;
//for循環(huán)找出Message的執(zhí)行時間大于now的節(jié)點(diǎn),
//退出循環(huán)
for (;;) {
n = p.next;
if (n == null) {
return;
}
if (n.when > now) {
break;
}
p = n;
}
p.next = null;
//while循環(huán)遍歷,調(diào)用Message.recycleUncheked()方法
//清除消息內(nèi)容,并從隊(duì)列中刪除
do {
p = n;
n = p.next;
p.recycleUnchecked();
} while ( n != null );
}
}
}
//循環(huán)遍歷,清除消息隊(duì)列中所有Message的內(nèi)容并從隊(duì)列中刪除
private void removeAllMessagesLocked() {
Message p = mMessages;
while (p != null) {
Message n = p.next;
p.recycleUnchecked();
p = n;
}
mMessages = null;
}
- 參數(shù)safe表示是否需要安全退出,true則會把未執(zhí)行(Message.when > now)的消息全部清除并刪除,待執(zhí)行的Message會繼續(xù)執(zhí)行完;false會直接把隊(duì)列中所有的Message給清除并刪除;
- 退出操作依然進(jìn)行了同步操作,屬于線程安全的;
- 如果隊(duì)列quitAllowed為false,表示不允許退出,如果強(qiáng)行退出,會拋出異常;一旦退出,隊(duì)列無法再次被使用;
MessageQueue里還有判斷隊(duì)列中是否有特定消息和移除特定消息的一系列重載方法、一些native方法等,有興趣的同學(xué)可以去看下,這里不再分析。
3.Handler
Handler是消息分發(fā)對象,可以發(fā)送Message和處理Message。
成員變量
//j是否發(fā)現(xiàn)潛在的內(nèi)存泄漏,默認(rèn)為false
private static final boolean FIND_POTENTIAL_LEAKS = false;
private static final String TAG = "Handler";
//靜態(tài)全局變量,主線程的Handler
private static Handler MAIN_THREAD_HANDLER = null;
//綁定的Looper對象
final Looper mLooper;
//綁定的MessageQueue消息隊(duì)列
final MessageQueue mQueue;
//回調(diào)接口的實(shí)現(xiàn)
final Callback mCallback;
//是否是異步的,如果是異步的,在發(fā)送消息的時候,
//會調(diào)用Message.setAsynchronous(true)把消息設(shè)為異步消息
final boolean mAsynchronous;
IMessenger mMessenger;
- 一個Handler與一個Looper和MessageQueue相關(guān)聯(lián)
Callback、dispatchMessage()方法
public interface Callback {
/**
* @param msg A {@link android.os.Message Message} object
* @return True if no further handling is desired
*/
public boolean handleMessage(Message msg);
}
/**
* Subclasses must implement this to receive messages.
*/
public void handleMessage(Message msg) {}
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
private static void handleCallback(Message message) {
message.callback.run();
}
- Callback是一個接口,可以在Handler對象初始化的時候把一個Callback對象傳進(jìn)去;
- 可以看出dispatchMessage處理消息的優(yōu)先級順序依次是
- 如果Message本身設(shè)置了Callback(是一個Runnable對象),則把Message交給Message自身的Callback來處理;
- 否則,如果Handler的設(shè)置了Callback回調(diào)(是Handler.Callback的實(shí)現(xiàn)類),則由Handler的Callback來處理,并返回一個返回值,表示是否消耗掉這個消息,不進(jìn)行后續(xù)處理(有點(diǎn)類似于消息傳遞機(jī)制的返回值);
- 如果上一步的返回值是false,則表示不消耗這個消息,交給Handler本身的handleMessage方法來處理,這個方法默認(rèn)實(shí)現(xiàn)是空方法,需要自行覆寫去處理消息;
構(gòu)造函數(shù)
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) {
//如果FIND_POTENTIAL_LEAKS設(shè)為true,如果Handler自身對象是匿名類、內(nèi)部成員
//類和本地類而且不是static的時候,會給出警告,可能有潛在的內(nèi)存泄漏風(fēng)險(xiǎn)
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());
}
}
//調(diào)用Looper.myLooper()方法獲得一個Looper對象,源碼下文分析
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException("Can't create handler inside thread that has not called Looper.prepare()");
}
//MessageQueue直接拿Looper對象里的mQueue對象
mQueue = mLooper.mQueue;
mCallback = callback;
//設(shè)置是否異步
mAsynchronous = async;
}
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
- 第6個重載方法,調(diào)用Looper.myLooper()方法獲得一個Looper對象,并從Looper對象里直接拿mQueue賦值給自身對象的mQueue;
- 構(gòu)造方法重載中,有可以直接設(shè)置Looper對象的,不一定是非得通過Looper.myLooper()來初始化mLooper對象,也有可能是外部獲得了直接傳進(jìn)來;
- 如果FIND_POTENTIAL_LEAKS設(shè)為true,如果Handler自身對象是匿名類、內(nèi)部成員類和本地類而且不是static的時候,會給出警告,可能有潛在的內(nèi)存泄漏風(fēng)險(xiǎn),所以建議Handler如果是內(nèi)部類的時候,建議使用靜態(tài)內(nèi)部類;
obtainMessage()方法
public final Message obtainMessage() {
return Message.obtain(this);
}
- Handler中有一系列obtanMessage()重載方法,只是傳的參數(shù)不同,其實(shí)最后調(diào)用的還是Message.obtain()方法,需要注意的是,在Message中,obtain()方法是靜態(tài)方法,在Handler中,是非靜態(tài)的,需要通過具體的Handler實(shí)例對象來獲得,但是禁止子類進(jìn)行覆寫;
post(Runnable r)相關(guān)方法
在Handler中,可以發(fā)送一個Runnable對象,也可以發(fā)送一個Message對象,現(xiàn)在我們先來分析發(fā)送Runnable對象的相關(guān)方法:
//立即post一個Runnable到MessageQueue中,此時Runnable被包裝正Message后放在MessageQueue隊(duì)列中(when == 當(dāng)前系統(tǒng)時間,可能是隊(duì)頭,也可能不是隊(duì)頭,隊(duì)列中已經(jīng)有when值小于當(dāng)前時間的Message)
public final boolean post(Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}
//Runnable包裝成Message后加入到MessageQueue中,但此時
//Message.when=uptimeMillis,uptimeMills是消息的執(zhí)行時間,
//即指定了具體的執(zhí)行系統(tǒng)時間
public final boolean postAtTime(Runnable r, long uptimeMillis) {
return sendMessageAtTime(getPostMessage(r), uptimeMillis);
}
//同上,只是又傳入了token對象,存儲在Message.obj中
public final boolean postAtTime(Runnable r, Object token, long uptimeMillis) {
return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
}
//Runnable包裝成Message后加入到MessageQueue中,
//但是Message.when等于當(dāng)前時間+delayMillis,
//表示延遲delayMills后執(zhí)行
public final boolean postDelayed(Runnable r, long delayMillis) {
return sendMessageDelayed(getPostMessage(r), delayMillis);
}
//Runnable包裝成Message后加入到MessageQueue中,此時when=0,
//一定是在MessageQueue的隊(duì)頭
public final boolean postAtFrontOfQueue(Runnable r) {
return sendMessageAtFrontOfQueue(getPostMessage(r));
}
//把Runnable對象包裝成Message對象,可見只是把Runnable對象
//賦值給了Message的callback域
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
//上述方法的重載方法,把token賦值給了Message的obj域,
//可以用這個方法進(jìn)行傳值
private static Message getPostMessage(Runnable r, Object token) {
Message m = Message.obtain();
m.obj = token;
m.callback = r;
return m;
}
//延遲發(fā)送,把當(dāng)前時間加上延遲時間后調(diào)用了sendMessageAtTime()方法
public final boolean sendMessageDelayed(Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
//發(fā)送消息的方法,對queue判空后,調(diào)用enqueueMessage進(jìn)行實(shí)際入隊(duì)
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);
}
//實(shí)際對消息入隊(duì)的方法,在該方法中,會把Message的target域進(jìn)行賦值,
//如果mAsynchronous是true,則會調(diào)用setter方法把消息設(shè)置為異步消息,
//調(diào)用的入隊(duì)方法其實(shí)是調(diào)用的MessageQueue的enqueueMessage方法
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
- post相關(guān)方法傳入的是Runnable對象,但是最終都會被包裝成Message對象,通過obtain()獲取一個Message對象,即把Runnable對象傳入Message的callback域,并可以傳入一個Object對象到Message的obj域進(jìn)行傳值;
- 發(fā)送消息其實(shí)就是把消息入隊(duì),實(shí)際的入隊(duì)操作其實(shí)是調(diào)用的MessageQueue對象的enqueueMessage操作,一系列的調(diào)用方法只是在計(jì)算Message的when值;
- 調(diào)用post()后,不一定消息就在MessageQueue的隊(duì)頭,也就意味著post的Runnable不一定會立即得到執(zhí)行,因?yàn)榇藭r消息的when值是當(dāng)前的系統(tǒng)時間,不能保證它前邊沒有在等待執(zhí)行的消息;如果想要立即被執(zhí)行,可以調(diào)用postAtFrontOfQueue(Runnable r)方法;
- 在實(shí)際的入隊(duì)方法enqueueMessage()中,會將傳入的Message對象的target對象賦值為自身的引用,如果mAsynchronous是true,則會調(diào)用setter方法把消息設(shè)置為異步消息,最后調(diào)用的MessageQueue的enqueueMessage方法對消息進(jìn)行入隊(duì)操作;上邊講到的MessageQueue中異步消息標(biāo)志位,就是在這設(shè)置位ture的;
sendMessage(Message msg)相關(guān)方法
public final boolean sendMessage(Message msg) {
return sendMessageDelayed(msg, 0);
}
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);
}
//...
//省略其他方法,基本上跟post系列方法是一一對應(yīng)的
- sendMessage方法的參數(shù)是Message對象,跟post系列方法的區(qū)別就是不用把Runnable封裝成Message,其實(shí)調(diào)用鏈最后都是 sendMessageAtTime() -> enqueueMessage();
- sendMessage方法和post方法的區(qū)別在于對消息的處理不同:
- post一個Runnable的時候,由于把Runnable對象賦值給了callback域,所以在Looper調(diào)用dispatchMessage()方法的時候,這個Runnable對象直接得到了執(zhí)行;而sendMessage系列的方法把消息發(fā)送后,在Looper調(diào)用dispatchMessage()方法的時候,由于Message的callback域可能為空,需要給Handler設(shè)置callback或者覆寫handlerMessage(Message msg)來處理消息;
- 我的理解是,post系列方法側(cè)重是想把某些操作給post到特定線程去執(zhí)行,而sendMessage系列的方法是想把值傳到特定線程,然后針對特定值去做某些處理;所以,post方法弱化傳值的操作,sendMessage方法強(qiáng)調(diào)的是傳值并對傳過去的值進(jìn)行操作;
runWithScissors(final Runnable r, long timeout)
除了post(Runnable r)系列方法和sendMessage(Message msg)系列方法可以發(fā)送消息外,還有runWithScissors可以發(fā)送消息(Runnable對象)。
/**
* Runs the specified task synchronously.
* <p>
* If the current thread is the same as the handler thread, then the runnable
* runs immediately without being enqueued. Otherwise, posts the runnable
* to the handler and waits for it to complete before returning.
* </p><p>
* This method is dangerous! Improper use can result in deadlocks.
* Never call this method while any locks are held or use it in a
* possibly re-entrant manner.
* </p><p>
* This method is occasionally useful in situations where a background thread
* must synchronously await completion of a task that must run on the
* handler's thread. However, this problem is often a symptom of bad design.
* Consider improving the design (if possible) before resorting to this method.
* </p><p>
* One example of where you might want to use this method is when you just
* set up a Handler thread and need to perform some initialization steps on
* it before continuing execution.
* </p><p>
* If timeout occurs then this method returns <code>false</code> but the runnable
* will remain posted on the handler and may already be in progress or
* complete at a later time.
* </p><p>
* When using this method, be sure to use {@link Looper#quitSafely} when
* quitting the looper. Otherwise {@link #runWithScissors} may hang indefinitely.
* (TODO: We should fix this by making MessageQueue aware of blocking runnables.)
* </p>
*
* @param r The Runnable that will be executed synchronously.
* @param timeout The timeout in milliseconds, or 0 to wait indefinitely.
*
* @return Returns true if the Runnable was successfully executed.
* Returns false on failure, usually because the
* looper processing the message queue is exiting.
*
* @hide This method is prone to abuse and should probably not be in the API.
* If we ever do make it part of the API, we might want to rename it to something
* less funny like runUnsafe().
*/
public final boolean runWithScissors(final Runnable r, long timeout) {
if (r == null) {
throw new IllegalArgumentException("runnable must not be null");
}
if (timeout < 0) {
throw new IllegalArgumentException("timeout must be non-negative");
}
//如果當(dāng)前的線程和Handler所在的線程是同一個,則直接運(yùn)行Runnable對象,
//而不需要再進(jìn)行入隊(duì)-排隊(duì)-出隊(duì)的操作;
if (Looper.myLooper() == mLooper) {
r.run();
return true;
}
//如果當(dāng)前線程和Handler的線程不在同一個線程,
//則會調(diào)用BlockingRunnable的postAndWait()方法
BlockingRunnable br = new BlockingRunnable(r);
return br.postAndWait(this, timeout);
}
//靜態(tài)內(nèi)部私有常量類,實(shí)現(xiàn)Runnable接口
private static final class BlockingRunnable implements Runnable {
//真正需要執(zhí)行的Rnnable對象
private final Runnable mTask;
//標(biāo)識是否執(zhí)行完畢
private boolean mDone;
public BlockingRunnable(Runnable task) {
mTask = task;
}
@Override public void run() {
try {
mTask.run(); //運(yùn)行mTask.Runnalbe
} finally {
synchronized(this) {
//執(zhí)行完以后,會把mDone標(biāo)志位置為true,并喚醒其他阻塞線程
mDone = true;
notifyAll();
}
}
}
public boolean postAndWait(Handler handler, long timeout) {
//把自身對象通過傳入的handler.post()方法進(jìn)行入隊(duì)操作,
//入隊(duì)成功返回true,失敗返回false;
if (!handler.post(this)) {
return false;
}
//加鎖同步
synchronized(this) {
if (timeout > 0) {
//計(jì)算超時時間,如果超時后還沒有執(zhí)行完,返回false,
//并執(zhí)行run中finally中的操作,
//即把標(biāo)志位置為true,并喚醒其他線程,此時run可能還沒有執(zhí)行完
final long expirationTime = SystemClock.uptimeMillis() + timeout;
while (!mDone) {
long delay = expirationTime - SystemClock.uptimeMillis();
if (delay <= 0) {
return false; // timeout
}
try {
wait(delay);
} catch(InterruptedException ex) {}
}
} else {
//如果timeout小于等于0,會阻塞handler所在線程,
//直到run方法內(nèi)的finallay塊被執(zhí)行(會一直等到run執(zhí)行完畢)
while (!mDone) {
try {
wait();
} catch(InterruptedException ex) {}
}
}
}
return true;
}
}
- 這個方法是消息的同步執(zhí)行方法,在執(zhí)行的時候會阻塞handler所在線程。執(zhí)行時可以設(shè)置超時時間,如果超時后,直接喚醒線程,停止執(zhí)行;如果超時時間小于等于0,則會一直等到run執(zhí)行完畢,finally塊得到執(zhí)行才能喚醒線程;
- runWithScissors()方法的返回值,true代表Runnable對象被正確執(zhí)行,false代表執(zhí)行失??;
- 源碼注釋寫的很清楚,這個方法非常的危險(xiǎn),不建議使用,因?yàn)椴划?dāng)?shù)氖褂每赡軙?dǎo)致死鎖。不要在持有任何鎖時調(diào)用此方法,也不要以可重入的方式使用它。
- 在后臺線程必須同步等待在處理程序的線程上運(yùn)行的任務(wù)完成的情況下,這種方法有時很有用。然而,這個問題通常是糟糕設(shè)計(jì)的癥狀。在使用這種方法之前,考慮改進(jìn)設(shè)計(jì)(如果可能的話)。
getMain()方法
/** @hide */
@NonNull public static Handler getMain() {
if (MAIN_THREAD_HANDLER == null) {
MAIN_THREAD_HANDLER = new Handler(Looper.getMainLooper());
}
return MAIN_THREAD_HANDLER;
}
/** @hide */
@NonNull public static Handler mainIfNull(@Nullable Handler handler) {
return handler == null ? getMain() : handler;
}
- 通過getMain()方法可以拿到主線程的Handler;
- mainIfNull(Handler hander)方法,相當(dāng)于是對傳入的handler進(jìn)行判空,如果為空,則返回主線程的Handler;
Handler中還有removeCallbacks()、removeMessage()、hasMessage()、toString()方法以及其重載方法,源碼比較簡單,這里就不做分析了,感興趣的同學(xué)可以自行去查看源碼。
4. Looper
Looper內(nèi)部有一個消息隊(duì)列MessageQueue,循環(huán)從MessageQueue中不斷取出Message并對其進(jìn)行分發(fā)。一個線程只能有一個Looper,由于Looper是線程局部變量,所以每個Looper中又有各自的MessageQueue。
成員變量
// sThreadLocal.get() will return null unless you've called prepare().
//靜態(tài)域,線程局部變量,每個線程中有一個獨(dú)立Looper對象。
//如果不調(diào)用prepare()方法,sThreadLocal.get()返回null
static final ThreadLocal < Looper > sThreadLocal = new ThreadLocal < Looper > ();
//靜態(tài)域,主線程的Looper,由Looper class來維護(hù)
private static Looper sMainLooper; // guarded by Looper.class
//消息隊(duì)列,每個Looper中有一個MessageQueue
final MessageQueue mQueue;
//Looper對象自身所在線程
final Thread mThread;
//打印對象及Tag
private Printer mLogging;
private long mTraceTag;
/* If set, the looper will show a warning log if a message dispatch takes longer than time. */
private long mSlowDispatchThresholdMs;
- sThreadLocal是一個線程本地變量,每一個線程獨(dú)自維護(hù)各自Looper的值,保證了每個線程都有一個Looper。關(guān)于ThreadLocal線程本地變量的原理這里不多展開講解了,不了解的同學(xué)可以自行查閱資料。這里需要注意,Java的ThreadLocal和Android的ThreadLocal的實(shí)現(xiàn)是不一樣的;
- 由于sThreadLocal是線程局部變量,Looper中所有的非靜態(tài)變量在每個線程都有單獨(dú)的副本,所以每個線程都有各自的MessageQueue;
- 初始化的時候,會把Looper所在線程信息記錄在mThread變量中;
構(gòu)造方法
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
- Looper的構(gòu)造方法是私有的,不能new出一個Looper,而是應(yīng)該調(diào)用prepare()方法來獲得一個Looper對象;
- 構(gòu)造方法很簡單,new了一個MessageQueue,并把當(dāng)前線程信息記錄下來。傳入的參數(shù)quitAllowed代表是否允許MessageQueue退出,如果不允許退出,調(diào)用MessageQueue的quit()方法時會拋出異常;
prepare()方法
//公有方法,開發(fā)者只能調(diào)用這個方法進(jìn)行Looper的初始化,
//可見開發(fā)者得到的Looper中的MessageQueue,是允許退出的
public static void prepare() {
prepare(true);
}
//私有方法,開發(fā)者無法調(diào)用,而且對同一個線程重復(fù)調(diào)用
//prepare()方法會拋出異常
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));
}
//主線程的Looper初始化,雖然是公有方法,我們無法調(diào)用,
//因?yàn)橄到y(tǒng)啟動的時候已經(jīng)調(diào)用過了,如果再次調(diào)用,會拋異常
public static void prepareMainLooper() {
prepare(false);
synchronized(Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
//把得到的主線程Looper賦值給sMainLooper
sMainLooper = myLooper();
}
}
- 調(diào)用Looper.prepare()會為當(dāng)前線程初始化一個Looper對象,Looper對象又會初始化一個MessageQueue對象;
- 主線程初始化Looper時創(chuàng)建的MessageQueue對象是禁止退出操作的,而其他線程初始化時Looper創(chuàng)建的MessagQueue是允許退出操作的。
- 一個線程只能調(diào)用一次prepare()方法,否則會拋出異常,所以在prepare()之前,應(yīng)該先調(diào)用myLooper()方法判斷是否為空;
- 主線程的Looper在ActivityThread中的main()方法進(jìn)行初始化的,
public static@Nullable Looper myLooper() {
return sThreadLocal.get();
}
loop()方法
//代碼省去打印等其他無關(guān)邏輯
public static void loop() {
//拿到本線程的sThreadLocal變量的值
final Looper me = myLooper();
//如果當(dāng)前線程沒有調(diào)用過prepare()方法,則me為null,拋出異常
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
//從me里獲得本線程的MessageQueue對象
final MessageQueue queue = me.mQueue;
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
//進(jìn)入死循環(huán),開始從MessageQueue中取出消息進(jìn)行處理
for (;;) {
//取出消息,可能被阻塞
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
try {
//通過Message對象中的targe域獲得發(fā)送該消息的Handler,
//調(diào)用它的dispatchMessage()方法來處理對象
msg.target.dispatchMessage(msg);
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
final long newIdent = Binder.clearCallingIdentity();
msg.recycleUnchecked();
}
}
- 拿到本線程的sThreadLocal變量的值,如果當(dāng)前線程沒有調(diào)用過prepare()方法,則me為null,拋出異常;
- 從me里獲得本線程的MessageQueue對象,進(jìn)入死循環(huán),開始從MessageQueue中取出消息進(jìn)行處理;
- 通過Message對象中的targe域獲得發(fā)送該消息的Handler,調(diào)用它的dispatchMessage()方法來處理對象,這也是為什么在入隊(duì)的時候,會對Message的target域進(jìn)行判空;
- 當(dāng)MessageQueue的next方法返回為null時,意味著MessageQueue已經(jīng)退出了(next()方法有分析),此時會退出死循環(huán),所以MessageQueue的退出,也會導(dǎo)致Looper的循環(huán)結(jié)束;
Looper還有quit(),quitSafely(),toString()等其他方法,感興趣的同學(xué)可以自行查看源碼;
5. 利用異步消息處理機(jī)制,主線程與子線程之間的通信
通過上述源碼的詳細(xì)分析,可以知道:
- 一個線程只能通過Looper.prepare()(建議不確定子線程是否已經(jīng)調(diào)過了,調(diào)用它之前可以先調(diào)用Looper.myLooper()判斷下,否則會拋異常)來產(chǎn)生一個Looper對象,同時也綁定了一個屬于該線程的MessageQueue對象;
- 一個Handler在構(gòu)造函數(shù)中,會獲取當(dāng)前線程中的Looper對象,并從Looper對象中獲取MessageQueue對象,所以在new一個Handler之前,要保證Looper.prepare()已經(jīng)調(diào)用過,否則會拋出異常;
- 一個消息,最終是在哪個線程被處理的,取決于這個Handler在哪個線程(通過new一個Handler方式得到的Looper和MessageQueue是在同一個線程的),最后分發(fā)Message被執(zhí)行的是在Looper.loop()方法的 msg.target.dispatchMessage(msg)語句來處理的,所以消息被拋給Handler去處理,也就是說消息被哪個線程的Handler發(fā)送,就會在哪個線程被執(zhí)行;
- 基于上述思路,主線程與子線程之間的通信就簡單明了了,子線程向主線程進(jìn)行通信,只需直接在主線程new一個Handler(在主線程中不可以調(diào)用Looper.prepare()方法),然后把消息發(fā)送過去就行:
private static Handler handler = new Handler() {
//主線程處理消息
@Override public void handleMessage(Message msg) {
//主線程根據(jù)Message進(jìn)行邏輯處理
}
};
class WorkThreadRunnable implements Runnable {
//子線程發(fā)送相關(guān)消息
@Override public void run() {
Message message = Message.obtain();
//在子線程中用在主線程中創(chuàng)建的Hander發(fā)送消息即可
handler.sendMessage(message);
}
}
- 主線程向子線程進(jìn)行通信,有兩種方式,一種是需要在子線程先調(diào)用Looper.prepare()方法初始化一個Looper和MessageQueue,然后在子線程創(chuàng)建一個Handler,并在主線程使用這個handler進(jìn)行發(fā)送消息,子線程接收到消息后進(jìn)行處理即可:
private Handler handler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler_main_to_child);
button = (Button) findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {@Override public void onClick(View view) {
Message message = Message.obtain();
handler.sendMessage(message);
}
});
new Thread(new WorkThreadRunnable()).start();
}
class WorkThreadRunnable implements Runnable {@Override public void run() {
Looper.prepare();
handler = new Handler() {@Override public void handleMessage(Message msg) {
//接收到消息后,進(jìn)行處理
}
};
Looper.loop();
}
}
- 主線程向子線程進(jìn)行通信的時候,Handler也不一定非得是在子線程進(jìn)行創(chuàng)建,另一種方式是在子線程創(chuàng)建Looper,在主線程new Handler的時候,把子線程的Looper當(dāng)參數(shù)傳入,這樣新建的Handler與Looper所在線程就進(jìn)行了綁定,這樣,主線程通過這個Handler依然可以向子線程進(jìn)行通信:
//創(chuàng)建子線程
class WorkThread extends Thread {
private Looper looper; //取出該子線程的Looper
public void run() {
Looper.prepare(); //創(chuàng)建該子線程的Looper
looper = Looper.myLooper(); //取出該子線程的Looper
Looper.loop(); //只要調(diào)用了該方法才能不斷循環(huán)取出消息
}
}
private Handler mHandler;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
thread = new WorkThread();
thread.start();
//創(chuàng)建Handler時把looper做入?yún)?,把Handler與Looper綁定在一起
mHandler = new Handler(thread.looper) {
public void handleMessage(android.os.Message msg) {
//在子線程中處理消息的邏輯
};
};
mHandler.senMessage(Message.obtain());
}
- 利用HandlerThread也可以實(shí)現(xiàn)線程間的通信,它其實(shí)是Thread和消息處理機(jī)制的一種封裝,后續(xù)文章再進(jìn)行分析
6. 異步消息處理機(jī)制使用不當(dāng)可能導(dǎo)致的問題
內(nèi)存泄漏
通常情況下,我們會在一個Activity/Fragment中使用消息處理機(jī)制進(jìn)行線程間的通信,如果Handler為非靜態(tài)內(nèi)部類,則會引用外部類對象。當(dāng)Activity/Fragment finish時,Handler可能并未執(zhí)行完,則會引起Activity/Fragment的內(nèi)存泄漏。故而所有調(diào)用Handler的地方,都使用靜態(tài)內(nèi)部類。如果Handler需要訪問Activity/Fragemnt非靜態(tài)成員/非靜態(tài)方法,可以讓Handler持有外部對象的弱引用;
private static class HandlerDemo extends Handler{
private final WeakReference<Activity> mActivity;
public HandlerDemo(Activvity mActivity) {
this.mActivity = new WeakRefrence<Activity>(mActivity);
}
@Override
public void handleMessage(Message msg) {
//處理消息,在訪問mActivity的時候要先進(jìn)行判空
}
}
資源被釋放而導(dǎo)致的異常
當(dāng)Activity/Fragment調(diào)用onDestroy()等回調(diào)方法后,會釋放掉一些資源,而在Handler執(zhí)行handleMessage()方法時,在訪問Activity/Fragment的成員變量/方法時,可能會由于資源被釋放而導(dǎo)致空異常。所以除了在對Activity/Fragment進(jìn)行判空以外,還要對訪問到的成員變量等進(jìn)行判空操作,并且在onDestory()方法里把消息隊(duì)列中的消息remove。
@Override
protected onDestroy() {\
super.onDestory();
//移除post的Runnable對象
handler.removeCallback(postRunnable);
//移除send的Message對象
handler.removeMessage(what);
}
7. 總結(jié)
通過上述源碼分析,我們理解了Message、MessageQueue、Handler和Looper的實(shí)現(xiàn)細(xì)節(jié),現(xiàn)在應(yīng)該對Android異步消息處理機(jī)制的實(shí)現(xiàn)的細(xì)節(jié)、通信原理和可能帶來問題都清楚了,如果還有什么疑問,大家可以自行查看源碼。如果有寫的不對的地方,歡迎進(jìn)行指正和交流。