網(wǎng)站改版的步驟軟件開發(fā)公司網(wǎng)站
目錄
一、前言
二、Handler對象?在新啟動的子線程發(fā)送消息(源碼跟蹤)
三、在主線程中,回調(diào) handleMessage 方法的流程是怎樣的呢?
四、總結(jié)說
1、主要由四個部分組成
①. Message (主要用于攜帶消息)
②. Handler (主要用于發(fā)送和處理消息)
???????③. MessageQueue (它是一個消息隊列)
???????④. Looper (它是一個循環(huán)器)
2、常見用法與源碼解讀
????????1、子線程
????????2、主線程
???????
系列文章
Handler異步消息傳遞機制(一)Handler常用基本用法
Handler異步消息傳遞機制(二)在子線程中創(chuàng)建Handler
Handler異步消息傳遞機制(三)在主線程、子線程中創(chuàng)建Handler,源碼(Android 9.0)解析
Handler異步消息傳遞機制(四)Handler發(fā)送消息流程,源碼(Android 9.0)解析
一、前言
上篇文章我們從源碼角度分析了如何在主線程、子線程創(chuàng)建Handler對象。詳細可參考:Handler異步消息傳遞機制(三)在主線程、子線程中創(chuàng)建Handler,源碼(Android 9.0)徹底解析?
那么創(chuàng)建Handler之后,如何發(fā)送消息呢?這個流程相信大家也已經(jīng)非常熟悉了,我們繼續(xù)以文章?Handler異步消息傳遞機制(一)Handler常用實現(xiàn)方式?的demo為例,然后進行源碼跟蹤解析!
二、Handler對象?在新啟動的子線程發(fā)送消息(源碼跟蹤)
下面是 Handler對象:在新啟動的子線程發(fā)送消息 ?的代碼:
public class DownLoadAppFile {public void download(String urlPath, Handler handler, ProgressBar pb) {try {//下載apk的代碼,這里用線程睡眠模擬Thread.currentThread().sleep(3*1000);} catch (InterruptedException e) {e.printStackTrace();}Message msg = Message.obtain();msg.what =1;//成功//msg.what =2;//失敗handler.sendMessage(msg);//發(fā)送消息}
}
Handler 到底是把 Message 發(fā)送到哪里去了呢?
為什么之后又可以在 Handler 的 handleMessage 方法中重新得到這條Message呢?
接下來我們來看一下發(fā)送消息的源碼:
public final boolean sendMessage(Message msg){return sendMessageDelayed(msg, 0);}
它里面調(diào)用了sendMessageDelayed方法,
往下追蹤
public final boolean sendMessageDelayed(Message msg, long delayMillis){if (delayMillis < 0) {delayMillis = 0;}return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);}
我們可以看到 sendMessageDelayed 方法,其中 msg 參數(shù)就是我們發(fā)送的 Message 對象,
而 delayMillis 參數(shù)則表示延遲發(fā)送消息的時間(毫秒),這里默認傳入的為0
往下追蹤
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 方法同樣接收兩個參數(shù),其中msg參數(shù)就是我們發(fā)送的 Message 對象,
而 uptimeMillis 參數(shù)則表示發(fā)送消息的時間,SystemClock.uptimeMillis() + delayMillis 即它的值等于自系統(tǒng)開機到當(dāng)前時間的毫秒數(shù)再加上延遲時間。
然后對 MessageQueue 對象 queue 進行了賦值,這個 MessageQueue 又是什么東西呢?
學(xué)過java基礎(chǔ)的可能會馬上想到Queue,基本上一個隊列就是一個先入先出(FIFO)的數(shù)據(jù)結(jié)構(gòu)。
同樣在這里 MessageQueue ?直譯過來就是?消息隊列 的意思,用于將所有收到的消息以隊列的形式進行排列,并提供入隊和出隊的方法。
那么 enqueueMessage 方法就是入隊的方法了,我們來看下這個方法的源碼:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {msg.target = this;if (mAsynchronous) {msg.setAsynchronous(true);}return queue.enqueueMessage(msg, uptimeMillis);}
msg.target = this;? 即msg.target 賦值為 this,也就是把當(dāng)前所在類 Handler,作為 msg 的 target 屬性。
然后將這三個參數(shù)都傳遞到 MessageQueue 的 enqueueMessage 方法中,
也就是說handler發(fā)出的消息,最終會保存到消息隊列中去。
調(diào)用 sendMessage 方法其實最后是調(diào)用了類 MessageQueueen 消息隊列的 enqueueMessage 入隊方法。
那么有了 MessageQueue 消息隊列的 enqueueMessage 入隊方法,它必然有相對應(yīng)的出隊方法。
Handler 對象在新啟動的子線程發(fā)送消息以后,接下來在主線程中,Handler類處理消息的方法 handleMessage 被自動回調(diào)。
三、在主線程中,回調(diào) handleMessage 方法的流程是怎樣的呢?
那么接下來在主線程中,回調(diào) handleMessage 方法的流程是怎樣的呢?
Android的主線程就是 ActivityThread,主線程的入口方法為main,我們繼續(xù)來看一下 ActivityThread 類中main方法的源代碼:
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 libcoreEventLogger.setReporter(new EventLoggingReporter());// Make sure TrustedCertificateStore looks in the right place for CA certificatesfinal File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());TrustedCertificateStore.setDefaultUserDirectory(configDir);Process.setArgV0("<pre-initialized>");Looper.prepareMainLooper();// Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line.// It will be in the format "seq=114"long startSeq = 0;if (args != null) {for (int i = args.length - 1; i >= 0; --i) {if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {startSeq = Long.parseLong(args[i].substring(PROC_START_SEQ_IDENT.length()));}}}ActivityThread thread = new ActivityThread();thread.attach(false, startSeq);if (sMainThreadHandler == null) {sMainThreadHandler = thread.getHandler();}if (false) {Looper.myLooper().setMessageLogging(newLogPrinter(Log.DEBUG, "ActivityThread"));}// End of event ActivityThreadMain.Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);Looper.loop();throw new RuntimeException("Main thread loop unexpectedly exited");}
第47行調(diào)用了 Looper.loop() 方法,這個方法內(nèi)部執(zhí)行了消息循環(huán),我們來看下它的源碼:
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();// Allow overriding a threshold with a system prop. e.g.// adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'final int thresholdOverride =SystemProperties.getInt("log.looper."+ Process.myUid() + "."+ Thread.currentThread().getName()+ ".slow", 0);boolean slowDeliveryDetected = false;for (;;) {Message msg = queue.next(); // might blockif (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 loggerfinal Printer logging = me.mLogging;if (logging != null) {logging.println(">>>>> Dispatching to " + msg.target + " " +msg.callback + ": " + msg.what);}final long traceTag = me.mTraceTag;long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;if (thresholdOverride > 0) {slowDispatchThresholdMs = thresholdOverride;slowDeliveryThresholdMs = thresholdOverride;}final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0);final boolean logSlowDispatch = (slowDispatchThresholdMs > 0);final boolean needStartTime = logSlowDelivery || logSlowDispatch;final boolean needEndTime = logSlowDispatch;if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {Trace.traceBegin(traceTag, msg.target.getTraceName(msg));}final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;final long dispatchEnd;try {msg.target.dispatchMessage(msg);dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;} finally {if (traceTag != 0) {Trace.traceEnd(traceTag);}}if (logSlowDelivery) {if (slowDeliveryDetected) {if ((dispatchStart - msg.when) <= 10) {Slog.w(TAG, "Drained");slowDeliveryDetected = false;}} else {if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",msg)) {// Once we write a slow delivery log, suppress until the queue drains.slowDeliveryDetected = true;}}}if (logSlowDispatch) {showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", 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();}}
我們可以看到前面幾行代碼,首先對 Looper、MessageQueue 對象進行了賦值,
然后第23行進入了一個死循環(huán)for(?; ;?){ },然后不斷地調(diào)用的 MessageQueue 的 next 方法,
Message msg = queue.next()?很明顯這個 next 方法就是消息隊列的出隊方法。
接下來你會發(fā)現(xiàn)第57行執(zhí)行了 msg.target.dispatchMessage(msg);
其中 msg.targe t就是指的 Handler 對象,你回看一下上面enqueueMessage()方法就可以看出來。
接下來當(dāng)然就要看一看 Handler 中 dispatchMessage 方法的源碼了,如下:
/*** 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);}}
在第8行進行判斷,如果 mCallback 不為空,則調(diào)用 mCallback 的 handleMessage 方法,
否則直接調(diào)用Handler的handleMessage方法,并將消息對象作為參數(shù)傳遞過去。
這樣我相信大家就都明白了為什么 handleMessage 方法中可以獲取到之前發(fā)送的消息了吧!
四、總結(jié)說
1、主要由四個部分組成
①. Message (主要用于攜帶消息)
在線程之間傳遞,可在內(nèi)部攜帶少量信息,用于不同線程之間交換數(shù)據(jù)
可以使用what、arg1、arg2字段攜帶整型數(shù)據(jù)
obj字段攜帶Object對象
???????②. Handler (主要用于發(fā)送和處理消息)
1、如果看過Handler源碼,你會知道 Handler 構(gòu)造器,
做了 Looper 對象是否為空的判定,
也就是創(chuàng)建Handler對象之前,必須擁有不為null的Looper對象。
所以子線程創(chuàng)建Handler后會報錯,它需要調(diào)用 prepare()方法。
2、我們平時可以直接在主線程中使用Handler,
那是因為在應(yīng)用程序啟動時,在入口的main方法中已經(jīng)默認為我們創(chuàng)建好了Looper。
主線程中內(nèi)部調(diào)用了Looper的prepareMainLooper方法,
而prepareMainLooper方法里面調(diào)用了Looper的prepare() 方法,所以不會報錯。
3、sendMessage方法用來發(fā)送消息,最終會回到handleMessage方法進行處理。
???????③. MessageQueue (它是一個消息隊列)
1、先入先出(FIFO)的數(shù)據(jù)結(jié)構(gòu)。
主要存放所有通過Handler發(fā)送的消息,
它們會一直存在于隊列中等待被處理
2、每個線程只有一個MessageQueue。
enqueueMessage 入隊方法,next 出隊方法
???????④. Looper (它是一個循環(huán)器)
調(diào)用loop方法后,會不斷從MessageQueue 取出待處理的消息,
然后傳遞到handleMessage進行處理
2、常見用法與源碼解讀
????????1、子線程
Handler對象調(diào)用 sendMessage 等方法發(fā)送消息,源碼內(nèi)部在調(diào)用過程中得到 MessageQueue 對象(它是先入先出的隊列),
其實最后是調(diào)用MessageQueue 消息隊列的 enqueueMessage 入隊方法,收到的消息以隊列的形式進行排列
????????2、主線程
Android 的主線程就是 ActivityThread,主線程的入口為main方法,main方法內(nèi)部:
? (1):Looper 調(diào)用 prepareMainLooper 靜態(tài)方法,內(nèi)部會去調(diào)用 prepare 方法(創(chuàng)建 一個Looper對象),
? ? ? ? ????方法內(nèi)部具體:?判讀是否有Looper對象? 有,提示 “每個線程只能創(chuàng)建一個?Looper對象”;沒有,創(chuàng)建一個新的Looper設(shè)置進去
?(2):Looper調(diào)用 loop 方法,loop方法內(nèi)部:
? ? ? ? ? ? 首先調(diào)用 myLooper 方法,去取一個Looper對象,
? ? ? ? ? ? 如果Looper對象為空,會拋出一個異常,提示沒有Looper對象;
? ? ? ? ? ? 如果Looper不為空,繼續(xù)執(zhí)行得到 MessageQueueen 消息隊列,然后進行 for循環(huán)操作,
? ? ? ? ? ?for循環(huán)里面是 MessageQueueen 對象調(diào)用 next 出隊方法,得到一個個Message 消息,調(diào)用dispatchMessage發(fā)送消息,
? ? ? ? ? ?最后通過回調(diào) handleMessage 方法并把 Message 消息依次傳遞過去。
這里采用網(wǎng)上的一張圖,個人感覺圖片概括得很好,就沒必要再去造同樣的輪子了,在新窗口打開可瀏覽大圖
不錯博文可參考:
Android異步消息處理機制完全解析,帶你從源碼的角度徹底理解_郭霖的專欄-CSDN博客_android 異步消息
Android進階——Android消息機制之Looper、Handler、MessageQueen_點擊置頂文章查看博客目錄(全站式導(dǎo)航)-CSDN博客_handler looper