Handler源碼解讀——handler使用時(shí)的注意事項(xiàng)
工作中經(jīng)常會(huì)遇到從子線程發(fā)送消息給主線程,讓主線程更新UI的操作,常見的有handler.sendMessage(Message),和handler.post(runnable)和handler.postDelayed(runnable, milliseconds);一直在使用這些方法,卻不知道他們的原理,今天就來解釋一下他們的原理。
先來一個(gè)常見的handler.sendMessage(message)的例子吧。代碼如下:
定義一個(gè)handler的子類,并實(shí)現(xiàn)它的handleMessage()方法:這里的msg.what的值是你自己定義的,就不多解釋了。
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case 1000:
int arg = msg.arg1;
// TODO: 17/3/22
break;
case 1001:
break;
default:
break;
}
}
};
接下來是在子線程中發(fā)送消息的部分:
//handler.sendMessage(Msg)
new Thread(new Runnable() {
@Override
public void run() {
Message msg = Message.obtain();
msg.what = 1000;
msg.arg1 = 10;
handler.sendMessage(msg);
}
}).start();
可以看到,我在一個(gè)新開的子線程中使用剛才定義的handler發(fā)送了一個(gè)Message對(duì)象,這個(gè)Message對(duì)象可以攜帶數(shù)據(jù),這里簡(jiǎn)單起見,我只攜帶了一個(gè)int類型的值10.
至于為什么在子線程中使用handler發(fā)送的Message,可以在UI線程中執(zhí)行了呢?這是因?yàn)樵贏ndroid的主線程中有一個(gè)Loop循環(huán)器,它一直在輪詢這個(gè)Loop循環(huán)器所屬的MessageQueue消息隊(duì)列中的消息,如果MessageQueue里面有Message,就調(diào)用這個(gè)Message.target.dispatchMessage(Message)方法,這里其實(shí)是調(diào)用了handler對(duì)象的dispatchMessage()方法,這個(gè)方法里面最終(滿足一定條件)會(huì)調(diào)用handler的handleMessage(Message)方法,也就是在主線程中調(diào)用了我們剛才定義的handler的handleMessage(Message)方法。
好吧,說了這么多,先來個(gè)精簡(jiǎn)版的簡(jiǎn)圖,接下來再用源碼解釋原理:
[圖片上傳失敗...(image-9ea33-1562745635646)]
ps: 上圖所涉及的源碼,可在Android Studio中可直接查看
1、ActivityThread類的main方法中,最后調(diào)用了Loop.loop()方法,開啟了主線程。
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
// 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());
// 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");
}
2、Loop.loop()方法中:
源代碼注解:在這個(gè)線程中一直調(diào)用MessageQuene隊(duì)列,確保loop被quit之前一直調(diào)用。
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
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
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);
}
// 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();
}
}
①獲取當(dāng)前線程的loop對(duì)象。
②獲取loop中的MessageQueue。
③使用for( ; ; )的開啟一個(gè)死循環(huán),開始輪詢MessageQueue中的消息。
④如果消息不為空,則調(diào)用Message對(duì)象的target的dispatchMessage(msg)方法
3、msg.target.dispatchMessage(msg)
Message類中:
/*package*/ Handler target;
①target對(duì)象為Handler類型,表示發(fā)送這個(gè)消息的handler對(duì)象
②可以通過obtain的一系列重載方法給message設(shè)置target,通過無參數(shù)的obtain方法獲取到的message的handler為null;
③target對(duì)象有set和get方法
public void setTarget(Handler target) {
this.target = target;
}
/**
* Retrieve the a {@link android.os.Handler Handler} implementation that
* will receive this message. The object must implement
* {@link android.os.Handler#handleMessage(android.os.Message)
* Handler.handleMessage()}. Each Handler has its own name-space for
* message codes, so you do not need to
* worry about yours conflicting with other handlers.
*/
public Handler getTarget() {
return target;
}
④至于我們這個(gè)handler發(fā)送的message對(duì)象中的target是如何與handler勾搭上的,留待后邊說明。
4、Handler.dispatchMessage(message)方法:
/**
* 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);
}
}
這段代碼表明,dispatchMessage方法中可能會(huì)調(diào)用handleMessage(msg)方法,不過需要滿足幾個(gè)條件。好吧,這個(gè)問題留待后邊解決。我們現(xiàn)在已經(jīng)簡(jiǎn)單的看完了handler在子線程發(fā)送消息,最終在主線程進(jìn)行處理的流程。接下來我們需要解決過程中遇到的幾個(gè)困惑。
困惑一:第3步時(shí),handler發(fā)送的message對(duì)象中的target是如何與這個(gè)handler勾搭上的。
困惑二:第4步時(shí),在dispatchMessage方法中,執(zhí)行handleMessage(msg)方法的條件如何解釋。
解答一:handler發(fā)送的message的target對(duì)象是如何與這個(gè)handler勾搭上的。
①我們得從handler發(fā)送消息說起,先來看我們?cè)谧泳€程中發(fā)送消息的代碼:來找找handler對(duì)象是何時(shí)設(shè)置給message.target的。
new Thread(new Runnable() {
@Override
public void run() {
Message msg = Message.obtain();
msg.what = 1000;
msg.arg1 = 10;
handler.sendMessage(msg);
}
}).start();
②點(diǎn)進(jìn)Message.obtain()方法:
/**
* Return a new Message instance from the global pool. Allows us to
* avoid allocating new objects in many cases.
*/
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();
}
里面的sPool是Message類型,是為了Message的復(fù)用,這里沒有設(shè)置target的數(shù)據(jù),再看最下邊的new Message();
/** Constructor (but the preferred way to get a Message is to call {@link #obtain() Message.obtain()}).
*/
public Message() {
}
好吧,這里是個(gè)空實(shí)現(xiàn),啥也沒有。
③既然在Message里面沒有找到,我們就在handler.sendMessage(msg)里面找找,點(diǎn)進(jìn)去:
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
沒有,繼續(xù)找:
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
繼續(xù):
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);
}
這里拿到了當(dāng)前的消息隊(duì)列,不過依然沒有我們要找的,繼續(xù):
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
好了,找到了。enqueueMessage()方法的第一步就給msg.target設(shè)置了值,就是當(dāng)前的handler對(duì)象,也就是發(fā)送消息的那個(gè)handler。這個(gè)方法的最后一句將這個(gè)msg添加進(jìn)當(dāng)前的MessageQueue,這樣,進(jìn)入隊(duì)列的message就攜帶上Handler類型的target對(duì)象了。
到現(xiàn)在為止,我們已經(jīng)說明,在Loop.loop()方法里的那句msg.target.dispatchMessage(msg),調(diào)用的就是當(dāng)前hander對(duì)象的dispatchMessage(msg)方法了。
解答二:在dispatchMessage方法中,執(zhí)行handleMessage(msg)方法的條件是怎么回事
①先上dispatchMessage(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);
}
}
具體流程如圖所示:
[圖片上傳失敗...(image-f7260-1562745635646)]
疑惑又來了,msg.callback是什么鬼?handleCallback(msg)方法是干嘛的?mCallback是什么鬼?它后邊的mCallback.handleMessage(msg)方法又是干嘛的?
②msg.callback和handleCallback(msg)方法;
進(jìn)入Message類中,發(fā)現(xiàn)callback成員變量為Runnable類型,Runnable接口自不必多說,它里面就一個(gè)無參無返回值的run方法;
在Message中是搜索callback,發(fā)現(xiàn)可以給它賦值的位置只有靜態(tài)方法obtain()的兩個(gè)重載方法:
public static Message obtain(Message orig) {
Message m = obtain();
m.what = orig.what;
m.arg1 = orig.arg1;
m.arg2 = orig.arg2;
m.obj = orig.obj;
m.replyTo = orig.replyTo;
m.sendingUid = orig.sendingUid;
if (orig.data != null) {
m.data = new Bundle(orig.data);
}
m.target = orig.target;
m.callback = orig.callback;
return m;
}
public static Message obtain(Handler h, Runnable callback) {
Message m = obtain();
m.target = h;
m.callback = callback;
return m;
}
再看一眼剛才的handleCallback(msg)方法:它就是調(diào)用message.callback.run方法。
private static void handleCallback(Message message) {
message.callback.run();
}
也就是說,只有當(dāng)我們直接通過Message.obtain(Message)和Message.obtain(Handler,Runnable)方法來獲取Message對(duì)象的時(shí)候(或者設(shè)置message.callback的值不為空),才有可能讓msg.callback不為空,才會(huì)調(diào)用callback.run方法,此時(shí)就絕對(duì)不會(huì)調(diào)用我們的handleMessage(msg)方法。
③接下來看mCallback和mCallback.handleMessage(msg)是何方神圣;
mCallback是Handler里面定義的一個(gè)Callback接口類型,
/**
* Callback interface you can use when instantiating a Handler to avoid
* having to implement your own subclass of Handler.
*
* @param msg A {@link android.os.Message Message} object
* @return True if no further handling is desired
*/
public interface Callback {
public boolean handleMessage(Message msg);
}
上方注釋的意思是:當(dāng)你實(shí)例化一個(gè)Handler的時(shí)候,可以通過這個(gè)可以接口避免自己寫一個(gè)Handler的實(shí)現(xiàn)類。
這句話是什么意思呢?且往下看。
在Handler類里面找給mCallback賦值的地方,找到三處,都是Handler的帶參構(gòu)造方法:
public Handler(Callback callback) {
this(callback, false);
}
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;
}
看到這兒,Handler里面這個(gè)接口的注釋應(yīng)該很好理解了。如果你使用上邊這兩個(gè)Handler的構(gòu)造方法來實(shí)例化一個(gè)Handler對(duì)象,那么你需要傳入一個(gè)Callback接口類型的參數(shù),在callback的handleMessage(msg)方法里面,你就可以直接寫主線程處理Message的邏輯了;此時(shí)我們就不需要再寫Handler的實(shí)現(xiàn)類,重寫handler的handleMessage(msg)方法了;另外,這個(gè)Callback接口里的handleMessage(msg)方法有返回值,如果你返回為true,就不會(huì)執(zhí)行handler.handleMessage(msg)方法,如果返回為false,依舊會(huì)執(zhí)行handler.handleMessage(msg)方法。這就給我們使用handler提供了多種選擇。
這里插一句嘴,上方的兩個(gè)Handler的構(gòu)造方法中,都有一個(gè)boolean類型的async參數(shù),同學(xué)們可能不知道怎么調(diào)用,沒關(guān)系,看看Handler()這個(gè)空參構(gòu)造是怎么寫的:
public Handler() {
this(null, false);
}
這個(gè)this(null,false)調(diào)用的就是Handler(Callback callback,boolean async)這個(gè)方法,也就是說,默認(rèn)的我們傳入的async這個(gè)參數(shù)為false就可以。至于這個(gè)參數(shù)何時(shí)為true,何時(shí)為false,這個(gè)問題留待后續(xù)研究,這次不做講解。
好,原理和流程解釋完了,來對(duì)關(guān)鍵流程做下總結(jié)。
[圖片上傳失敗...(image-ddd051-1562745635646)]
handler.dispatchMessage(msg)里面的處理,前面已經(jīng)解釋過了,這里不再重復(fù)。
使用總結(jié):
①在使用handler發(fā)送消息的時(shí)候,要記住,自己實(shí)現(xiàn)的handleMessage(msg)方法不一定會(huì)執(zhí)行。
②如果我們發(fā)送的Message的callback參數(shù)(Runnable類型)不為空,那么就不會(huì)執(zhí)行handler的handleMessage(msg)方法,只會(huì)執(zhí)行message.callback的run方法。這種情況主要發(fā)生在我們使用handler.post(runnable)和handler.postDelayed(runnable, milliseconds)方法發(fā)送消息的時(shí)候。另外,在Message對(duì)象的callback的參數(shù)不為空的情況下,如果我們?cè)趧?chuàng)建Handler對(duì)象的時(shí)候既傳入了Callback接口參數(shù),也重寫了里面的handleMessage(msg)方法,那么此時(shí)依舊只會(huì)執(zhí)行message.callback的run方法。
③在我們發(fā)送的Message的callback參數(shù)(Runnable類型)為空的情況下(默認(rèn)為空),在使用Handler的帶參構(gòu)造方法初始化Handler的時(shí)候,如果傳入了Callback類型的參數(shù),并且那么這個(gè)接口里面的方法肯定會(huì)被調(diào)用;如果同時(shí)我們也重寫了該handler對(duì)象的handleMessage(msg)方法,那么handler對(duì)象的handleMessage(msg)方法不一定執(zhí)行,取決于Callback接口中handleMessage(msg)方法的返回值,如果為true,那么handler對(duì)象的handleMessage(msg)方法不會(huì)執(zhí)行,為false時(shí),handler對(duì)象的handleMessage(msg)方法會(huì)在后邊執(zhí)行。
關(guān)于handler的總結(jié),希望對(duì)你有用。