Android線程間通信Handler源碼詳解
前言
在【Android】線程間通信 - Handler之使用篇主要講了 Handler 的創(chuàng)建,發(fā)送消息,處理消息 三個(gè)步驟。那么接下來(lái),我們也按照這三個(gè)步驟,從源碼中去探析一下它們具體是如何實(shí)現(xiàn)的。本篇是關(guān)于創(chuàng)建源碼的分析。
01、 用法
先回顧一下,在主線程和非主線程是如何創(chuàng)建 Handler 的。
//主線程 private val mHandler: Handler = object : Handler(Looper.getMainLooper()) { override fun handleMessage(msg: Message) { when (msg.what) { 1 -> {} } } }
//子線程 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è)類中獲取的。那讓我們跟進(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è)線程只能調(diào)用一次
prepare()
,這也就意味著每個(gè)線程只有一個(gè) Looper 。
可是看回在主線程中創(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.")
異常。 - 但是如果不是為主線程創(chuàng)建 Handler 的時(shí)候,我們就需要手動(dòng)調(diào)用
Looper.prepare()
, 否則該線程中的 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線程間通信Handler源碼詳解的詳細(xì)內(nèi)容,更多關(guān)于Android線程間通信Handler的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
RecyclerView實(shí)現(xiàn)橫向滾動(dòng)效果
這篇文章主要為大家詳細(xì)介紹了RecyclerView實(shí)現(xiàn)橫向滾動(dòng)效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-01-01Android this與Activity.this的區(qū)別
這篇文章主要介紹了 Android this與Activity.this的區(qū)別的相關(guān)資料,需要的朋友可以參考下2016-09-09Android實(shí)例HandlerThread源碼分析
本篇文章主要給大家通過(guò)實(shí)例代碼分析了Android中HandlerThread的用法以及步驟,需要的朋友參考學(xué)習(xí)下吧。2017-12-12Android中ListView下拉刷新的實(shí)現(xiàn)代碼
這篇文章主要介紹了Android中ListView下拉刷新的實(shí)現(xiàn)代碼的相關(guān)資料,需要的朋友可以參考下2017-06-06詳解 Android中Libgdx使用ShapeRenderer自定義Actor解決無(wú)法接收到Touch事件的問(wèn)題
這篇文章主要介紹了詳解 Android中Libgdx使用ShapeRenderer自定義Actor解決無(wú)法接收到Touch事件的問(wèn)題的相關(guān)資料,希望通過(guò)本文能幫助到大家解決這樣的問(wèn)題,需要的朋友可以參考下2017-09-09Android帶數(shù)字或紅點(diǎn)的底部導(dǎo)航攔和聯(lián)網(wǎng)等待加載動(dòng)畫示例
這篇文章主要介紹了Android帶數(shù)字或紅點(diǎn)的底部導(dǎo)航攔和聯(lián)網(wǎng)等待加載動(dòng)畫示例,具有一定的參考價(jià)值,有興趣的同學(xué)可以了解一下。2017-03-03Android 仿摩拜單車共享單車進(jìn)度條實(shí)現(xiàn)StepView效果
這篇文章主要介紹了android 仿摩拜單車共享單車進(jìn)度條實(shí)現(xiàn)StepView效果的實(shí)例,通過(guò)定義五個(gè)狀態(tài),分別為:為完成、正在進(jìn)行、已完成、終點(diǎn)完成、終點(diǎn)未完成。具體實(shí)現(xiàn)代碼,大家參考下2017-03-03Android實(shí)現(xiàn)簡(jiǎn)單的下拉刷新pulltorefresh
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)簡(jiǎn)單的下拉刷新pulltorefresh的相關(guān)代碼,具有一定的實(shí)用性和操作價(jià)值,感興趣的小伙伴們可以參考一下2016-07-07Android實(shí)現(xiàn)仿Windows7圖片預(yù)覽窗格效果
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)仿Windows7圖片預(yù)覽窗格效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-12-12