Android線(xiàn)程間通信Handler源碼詳解
前言
在【Android】線(xiàn)程間通信 - Handler之使用篇主要講了 Handler 的創(chuàng)建,發(fā)送消息,處理消息 三個(gè)步驟。那么接下來(lái),我們也按照這三個(gè)步驟,從源碼中去探析一下它們具體是如何實(shí)現(xiàn)的。本篇是關(guān)于創(chuàng)建源碼的分析。
01、 用法
先回顧一下,在主線(xiàn)程和非主線(xiàn)程是如何創(chuàng)建 Handler 的。
//主線(xiàn)程
private val mHandler: Handler = object : Handler(Looper.getMainLooper()) {
override fun handleMessage(msg: Message) {
when (msg.what) {
1 -> {}
}
}
}
//子線(xiàn)程
Thread {
Looper.prepare()
val handler = object : Handler() {
override fun handleMessage(msg: Message) {
when (msg.what) {
1 -> {}
}
}
}
handler.sendEmptyMessage(1)
Looper.loop()
}.start()
02、源碼
Handler 一共有 7 個(gè)構(gòu)造方法。但最后都會(huì)直接或間接使用到以下兩個(gè)構(gòu)造方法。所以我們看看兩個(gè)方法都做了什么事情吧。
//方法 1
//Handler.java
public Handler(@Nullable 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(); \\標(biāo)識(shí) 1
if (mLooper == null) { \\ 標(biāo)識(shí) 3
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue; \\標(biāo)識(shí) 2
mCallback = callback;
mAsynchronous = async;
}
//方法 2
public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
方法 2 更加的簡(jiǎn)單,那么我們就從它入手吧。在其中對(duì) 4 個(gè)變量進(jìn)行賦值。分別是 mLooper, mQueue, mCallback, mAsynchronous。方法 1 主要也是對(duì) 4 個(gè)變量進(jìn)行賦值。它們有什么作用,我們先不管,后面會(huì)講到。我們先來(lái)看看這方法 1 和方法 2 的區(qū)別是什么?
讓我們聚焦到標(biāo)識(shí) 1 和標(biāo)識(shí) 2,mLooper, mQueue 來(lái)源是通過(guò) Looper 這個(gè)類(lèi)中獲取的。那讓我們跟進(jìn)去看看。
//跟進(jìn)標(biāo)識(shí) 1
//Looper.java
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
看到是通過(guò) mThreadLocal.get() 獲得一個(gè) Looper 實(shí)例。那么 mThreadLocal 的 Looper 又是哪里來(lái)的呢?讓我們找找 mThreadLocal.set() 方法,就知道了!
//Looper.java
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));
}
原來(lái)是在 Looper.prepare() 中添加的。
這里需要注意!
- "Only one Looper may be created per thread",每個(gè)線(xiàn)程只能調(diào)用一次
prepare(),這也就意味著每個(gè)線(xiàn)程只有一個(gè) Looper 。
可是看回在主線(xiàn)程中創(chuàng)建 Handler 的時(shí)候,我們并沒(méi)有調(diào)用 Looper.prepare() 方法。但是也正常運(yùn)行了。那是為什么呢?那是因?yàn)樵?ActivityThread 中的 main()已經(jīng)調(diào)用了。
//ActivityThread.java
public static void main(String[] args) {
//...省略無(wú)關(guān)代碼
Looper.prepareMainLooper(); \\標(biāo)識(shí) 4
//...
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
//...
Looper.loop();
}
//跟進(jìn)標(biāo)識(shí) 4
//Looper.java
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
這里需要注意!
- 我們不僅不用調(diào)用,也不能調(diào)用。否則將會(huì)觸發(fā)
IllegalStateException("The main Looper has already been prepared.")異常。 - 但是如果不是為主線(xiàn)程創(chuàng)建 Handler 的時(shí)候,我們就需要手動(dòng)調(diào)用
Looper.prepare(), 否則該線(xiàn)程中的 Looper 為空,會(huì)觸發(fā)標(biāo)識(shí) 3 的代碼塊。 - 即會(huì)得到
RuntimeException("Can't create handler inside thread " + Thread.currentThread()+ " that has not called Looper.prepare()")異常。
03、結(jié)語(yǔ)

圖源 |《第一行代碼》
最后結(jié)合《第一行代碼》中異步消息處理機(jī)制的流程圖。我們可以看出 Handler 創(chuàng)建過(guò)程主要是準(zhǔn)備好了 Looper, MessageQueue 和 Handler 本身。
以上就是Android線(xiàn)程間通信Handler源碼詳解的詳細(xì)內(nèi)容,更多關(guān)于Android線(xiàn)程間通信Handler的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Android利用CountDownTimer實(shí)現(xiàn)驗(yàn)證碼倒計(jì)時(shí)效果實(shí)例
這篇文章主要給大家介紹了關(guān)于Android如何利用CountDownTimer實(shí)現(xiàn)驗(yàn)證碼倒計(jì)時(shí)效果的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2017-10-10
Android利用GridView實(shí)現(xiàn)單選效果
本篇文章主要介紹了Android利用GridView實(shí)現(xiàn)單選效果的相關(guān)知識(shí),具有很好的參考價(jià)值。下面跟著小編一起來(lái)看下吧2017-05-05
Android實(shí)現(xiàn)自定義加載框的代碼示例
本篇文章主要介紹了Android實(shí)現(xiàn)自定義加載框的代碼示例,App在與服務(wù)器進(jìn)行網(wǎng)絡(luò)交互的時(shí)候,有個(gè)提示加載框,有興趣的可以了解一下。2017-02-02
Flutter仿微信通訊錄實(shí)現(xiàn)自定義導(dǎo)航條的示例代碼
某些頁(yè)面比如我們?cè)谶x擇聯(lián)系人或者某個(gè)城市的時(shí)候需要快速定位到我們需要的選項(xiàng),一般都會(huì)需要像微信通訊錄右邊有一個(gè)導(dǎo)航條一樣的功能,本文將利用Flutter實(shí)現(xiàn)這一效果,需要的可以參考一下2022-04-04
Android?Compose之Animatable動(dòng)畫(huà)停止使用詳解
這篇文章主要為大家介紹了Android?Compose之Animatable動(dòng)畫(huà)停止使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03
Kotlin語(yǔ)言使用BroadcastReceiver示例介紹
Android開(kāi)發(fā)的四大組件分別是:活動(dòng)(activity),用于表現(xiàn)功能;服務(wù)(service),后臺(tái)運(yùn)行服務(wù),不提供界面呈現(xiàn);廣播接受者(Broadcast Receive),勇于接收廣播;內(nèi)容提供者(Content Provider),支持多個(gè)應(yīng)用中存儲(chǔ)和讀取數(shù)據(jù),相當(dāng)于數(shù)據(jù)庫(kù),本篇著重介紹廣播組件2022-09-09
Android Http實(shí)現(xiàn)文件的上傳和下載
這篇文章主要為大家詳細(xì)介紹了Android Http實(shí)現(xiàn)文件的上傳和下載,感興趣的小伙伴們可以參考一下2016-08-08
Android Studio 修改類(lèi)的默認(rèn)注釋圖文教程
這篇文章主要介紹了Android Studio 修改類(lèi)的默認(rèn)注釋圖文教程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-03-03
Android實(shí)現(xiàn)ListView異步加載的方法(改進(jìn)版)
這篇文章主要介紹了Android實(shí)現(xiàn)ListView異步加載的方法,針對(duì)前面介紹的方法進(jìn)行了線(xiàn)程操作的改進(jìn),具有一定參考借鑒價(jià)值,需要的朋友可以參考下2016-08-08

