網(wǎng)站首頁 編程語言 正文
前言
在【Android】線程間通信 - Handler之使用篇主要講了 Handler 的創(chuàng)建,發(fā)送消息,處理消息 三個步驟。那么接下來,我們也按照這三個步驟,從源碼中去探析一下它們具體是如何實現(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òu)造方法。但最后都會直接或間接使用到以下兩個構(gòu)造方法。所以我們看看兩個方法都做了什么事情吧。
//方法 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)識 1 if (mLooper == null) { \\ 標(biāo)識 3 throw new RuntimeException( "Can't create handler inside thread " + Thread.currentThread() + " that has not called Looper.prepare()"); } mQueue = mLooper.mQueue; \\標(biāo)識 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 更加的簡單,那么我們就從它入手吧。在其中對 4 個變量進行賦值。分別是 mLooper, mQueue, mCallback, mAsynchronous
。方法 1 主要也是對 4 個變量進行賦值。它們有什么作用,我們先不管,后面會講到。我們先來看看這方法 1 和方法 2 的區(qū)別是什么?
讓我們聚焦到標(biāo)識 1 和標(biāo)識 2,mLooper, mQueue 來源是通過 Looper 這個類中獲取的。那讓我們跟進去看看。
//跟進標(biāo)識 1 //Looper.java public static @Nullable Looper myLooper() { return sThreadLocal.get(); }
看到是通過 mThreadLocal.get()
獲得一個 Looper 實例。那么 mThreadLocal
的 Looper 又是哪里來的呢?讓我們找找 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)); }
原來是在 Looper.prepare()
中添加的。
這里需要注意!
- "Only one Looper may be created per thread",每個線程只能調(diào)用一次
prepare()
,這也就意味著每個線程只有一個 Looper 。
可是看回在主線程中創(chuàng)建 Handler 的時候,我們并沒有調(diào)用 Looper.prepare()
方法。但是也正常運行了。那是為什么呢?那是因為在 ActivityThread 中的 main()
已經(jīng)調(diào)用了。
//ActivityThread.java public static void main(String[] args) { //...省略無關(guān)代碼 Looper.prepareMainLooper(); \\標(biāo)識 4 //... ActivityThread thread = new ActivityThread(); thread.attach(false, startSeq); if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler(); } //... Looper.loop(); }
//跟進標(biāo)識 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)用。否則將會觸發(fā)
IllegalStateException("The main Looper has already been prepared.")
異常。 - 但是如果不是為主線程創(chuàng)建 Handler 的時候,我們就需要手動調(diào)用
Looper.prepare()
, 否則該線程中的 Looper 為空,會觸發(fā)標(biāo)識 3 的代碼塊。 - 即會得到
RuntimeException("Can't create handler inside thread " + Thread.currentThread()+ " that has not called Looper.prepare()")
異常。
03、結(jié)語
圖源 |《第一行代碼》
最后結(jié)合《第一行代碼》中異步消息處理機制的流程圖。我們可以看出 Handler 創(chuàng)建過程主要是準備好了 Looper, MessageQueue 和 Handler 本身。
原文鏈接:https://juejin.cn/post/7155791766631743524
相關(guān)推薦
- 2022-06-04 Dashboard管理Kubernetes集群與API訪問配置_云和虛擬化
- 2022-04-10 Blazor實現(xiàn)數(shù)據(jù)驗證_基礎(chǔ)應(yīng)用
- 2023-01-19 詳解如何利用C#實現(xiàn)設(shè)置系統(tǒng)時間_C#教程
- 2022-04-15 Go?interface{}?轉(zhuǎn)切片類型的實現(xiàn)方法_Golang
- 2022-03-23 C++控制權(quán)限關(guān)鍵字protected_C 語言
- 2022-05-08 利用Pandas讀取某列某行數(shù)據(jù)之loc和iloc用法總結(jié)_python
- 2022-10-08 jQuery動態(tài)添加元素后元素注冊事件失效解決_jquery
- 2022-07-04 C#實現(xiàn)進制轉(zhuǎn)換_C#教程
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡單動態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支