【Android】Handler 机制 ( Handler | Message | Looper | MessageQueue )
文章目錄
- I . Handler 機制簡介
- II . Handler 機制 Handler Message Looper MessageQueue 四組件對應關系
- III . Handler ( 消息處理者 )
- IV . Looper ( 消息遍歷者 )
- V . Looper 子線程
- VI . Handler 發(fā)送 消息 種類
- VII . Handler 機制總結
I . Handler 機制簡介
Handler 機制是 Android 中最重要的 異步通信 機制 ;
1 . Handler 機制作用 : 將需要執(zhí)行的任務分配給其它線程 ;
① 子線程更新 UI : 在子線程中更新 UI , 就是在子線程中將刷新 UI 的任務分配給了主線程 ; ( 子線程刷新 UI 會崩潰 )
② 主線程網絡操作 : 在主線程中 , 將網絡通信等耗時的操作分配給子線程 ( 該子線程需要轉成 Looper 線程 ) , 避免 UI 卡頓 ; ( 主線程訪問網絡會崩潰 )
2 . Handler 機制中涉及到的組件 :
① Handler ( 消息處理者 ) : 定義具體的代碼操作邏輯 , 處理收到消息 ( Message ) 后的具體操作 ;
② Message ( 消息 ) : 定義具體消息 , 其中可以封裝不同的變量 , 為 Handler 指定操作的類型 , 或執(zhí)行操作所需的數(shù)據 ;
③ Looper ( 消息遍歷者 ) : 消息的遍歷者 , 遍歷 MessageQueue 中的消息 , 分發(fā)給 Handler 處理 ;
④ MessageQueue ( 消息隊列 ) : 封裝在 Looper 中 , 每個 Looper 中封裝了一個 MessageQueue , 是 Looper 消息遍歷的重要組件 , 用戶不直接調用該組件 ;
3 . Handler 機制中的 封閉性 與 線程交互 :
① 線程內部相對封閉的運行系統(tǒng) : 整個 Looper 線程內部是一個封閉運行的系統(tǒng) , Looper 一直不停的再遍歷 MessageQueue , 將 消息 或 操作 取出 , 交給 Handler 執(zhí)行 ;
② 線程交互 : Handler 還有另外一個職責就是負責與外部線程的交互 , 在外部線程中調用 Handler 將消息回傳給本 Looper 線程 , 放入 MessageQueue 隊列中 ;
4 . Message ( 消息 ) 的運行路徑 ( 重點 ) : 在外部線程中 , 調用 Looper 線程的 Handler 成員 , 將 Message ( 消息 ) 發(fā)送給 Looper 線程中的 MessageQueue ( 消息隊列 ) , 然后 Looper 輪詢該 消息隊列時 , 又將該消息交給 Handler 進行處理 ;
Message -> Handler ( 發(fā)送 ) -> MessageQueue ( 存儲 ) -> Looper ( 輪詢 ) -> Handler ( 執(zhí)行 )
II . Handler 機制 Handler Message Looper MessageQueue 四組件對應關系
Handler , Message , Looper , MessageQueue 四組件對應關系 :
Handler 機制中的上述四者的對應關系 : 一個線程中只能有一個 Looper 及 Looper 中封裝的 MessageQueue , 每個 Looper 可以為多個 Handler 調度消息 , Message 消息可以有無數(shù)個 ;
Looper 是 線程本地存儲的對象 ( ThreadLocal ) , 一個線程只能存在一個 , MessageQueue ( 消息隊列 ) 定義在 Looper 內部 , 每個 Looper 中只定義了一個 MessageQueue ( 消息隊列 ) , 因此每個線程也只能有一個 MessageQueue ;
線程 與 Looper ( 消息遍歷者 ) 是一對一關系 , Looper ( 消息遍歷者 ) 與 MessageQueue ( 消息隊列 ) 是一對一的關系 , Looper ( 消息遍歷者 ) 與 Handler ( 消息處理者 ) 是一對多的關系 , Message ( 消息 ) 可以有很多 ;
III . Handler ( 消息處理者 )
1 . Handler 創(chuàng)建 : 這里注意 只能在 Looper 線程中創(chuàng)建 Handler , 普通線程不能創(chuàng)建 Handler ;
① 主線程 : 主線程中可以直接創(chuàng)建 Handler , 因為在點擊應用圖標后就會 啟動主線程 ActivityThread , 此時就已經將 Looper 實例化好了 , 因此我們在 Activity 中 , 可以任意創(chuàng)建多個 Handler , 并直接使用 ;
public final class ActivityThread {...public static void main(String[] args) {...Looper.prepareMainLooper();//創(chuàng)建 ActivityThread 線程, 并運行ActivityThread thread = new ActivityThread();//attach 方法 進行 thread 的最初初始化操作 thread.attach(false);...Looper.loop();...}//main... }//ActivityThread② 子線程 : 子線程如果要創(chuàng)建 Handler , 需要先 調用 Looper.prepare() 方法 , 將線程轉為 Looper 線程 , 因為 創(chuàng)建 Handler 時 , 會關聯(lián)線程的 Looper 對象 , 普通的子線程是沒有 Looper 對象的 , 調用 Looper.prepare() 方法即可為該線程創(chuàng)建 Looper 對象 , 該線程也就轉為了 Looper 線程 ;
public class handler {...//獲取 Looper 對象后 , 可以從 Looper 對象中獲取 MessageQueue//關聯(lián)后 , Handler 發(fā)送消息時 , 才能將消息精準的發(fā)送給final MessageQueue mQueue;//Handler 需要與線程的唯一 Looper 對象關聯(lián)final Looper mLooper; ...public Handler() {...mLooper = Looper.myLooper()if (mLooper == null) {throw new RuntimeException("Can't create handler inside thread that has not called Looper.prepare()");}...mQueue = mLooper.mQueue;...}... }2 . Handler 對象個數(shù) : 每個 Looper 線程可以創(chuàng)建多個 Handler , 只要該 Handler 與 Looper 和 MessageQueue 關聯(lián) , 就可以將 消息 ( Message ) 發(fā)送給 Looper 線程中的 MessageQueue 中 ; Looper 輪詢該 消息隊列 ( MessageQueue ) , 將消息再次分發(fā)給對應的 Handler 進行處理 ;
IV . Looper ( 消息遍歷者 )
1 . Looper 線程 : 如果要將 Handler , Looper 機制引入到線程中 , 使某線程具有接收 Message ( 消息 ) , 執(zhí)行某項操作的功能 , 需要將該線程轉為 Looper 線程 ;
2 . Looper 線程可執(zhí)行的操作 : 一個線程如果被轉為 Looper 線程 , 那這個線程運行后只能接收 Message 消息 , 執(zhí)行對應的操作 , 運行后永遠卡在 loop 循環(huán)遍歷的 while (true) 循環(huán)中 , 使用 quit() 方法才能退出 ;
3 . Loop.prepare() 方法 : 該方法是將 普通子線程 轉為 Looper 線程最終要的方法 , 該方法的主要作用是 創(chuàng)建 Looper , 然后將 Looper 對象放入 ThreadLocal 對象中存儲 ; 線程只有創(chuàng)建了 Looper 對象才能創(chuàng)建 Handler , 將該 Looper 對象及其中封裝的 MessageQueue 與 Handler 進行關聯(lián) , Handler 才可以進行消息的調度 ; 如果線程中沒有 Looper 對象 , 創(chuàng)建 Handler 會報運行時異常 ;
public final class Looper {...// sThreadLocal.get() will return null unless you've called prepare().@UnsupportedAppUsagestatic final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();...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));}... }4 . Loop.loop() 方法 : 一旦調用了該方法 , 就意味著該 Looper 線程進入到了輪詢 MessageQueue 的階段 , 這是一個無限死循環(huán) , 調用了該方法后 , Handler 發(fā)送消息 , 線程才能處理對應的業(yè)務邏輯 ;
調用 quit() 方法 , 可以終止該遍歷 MessageQueue 操作 ;
下面代碼刪除了大部分代碼 , 只留下 循環(huán)遍歷 和 調度 Message 信息給 Handler 進行處理 ;
public static void loop() {final Looper me = myLooper();...final MessageQueue queue = me.mQueue;...for (;;) {Message msg = queue.next(); // 阻塞if (msg == null) {// 循環(huán)遍歷退出return;}...try {//調度 Message 信息給 Handler 進行處理 msg.target.dispatchMessage(msg);...} ...} }V . Looper 子線程
1 . Looper 線程就是在普通線程的基礎是哪個 , 加入了 Looper 用于消息調度 , 然后將消息轉發(fā)給 Handler 進行處理 , 這樣就實現(xiàn)了 在其它線程中 , 將任務異步分配給該 Looper 線程 ;
2 . Android 中的主線程本身就是 Looper 線程 , 整個 Looper 循環(huán)遍歷消息的過程由系統(tǒng)完成 , 用戶只需要自定義一個 Handler 成員 , 即可在子線程中調用該 Handler 將消息發(fā)送到主線程 , 在主線程執(zhí)行相關操作 , 實現(xiàn)了將異步任務分配給主線程 , 這是子線程刷新 UI 的重要途徑 ;
3 . 普通子線程 轉為 Looper 子線程 流程 :
① 定義 Handler 成員變量 : 在線程 Thread 派生類中 , 定義 Handler 類型的成員變量 ;
② Looper 初始化 : 調用 Looper.prepare() 靜態(tài)方法 , 該方法的作用是創(chuàng)建 Looper 變量 , 存儲在了 ThreadLocal 中 , 將當前普通線程轉為 Looper 線程 ;
③ 實例化 Handler 成員 : 一定要在 Looper.prepare() 之后實例化成員 , 因為如果在之前實例化 , Handler 與 Looper 無法產生關聯(lián) ;
④ 輪詢消息隊列 : 調用 Looper.loop() 方法 , 輪詢消息隊列 ( MessageQueue ) ;
4 . 實例化 Handler 成員時機 :
① 實例化時機 : 必須要在調用 Looper.prepare() 之后實例化才可以 , 因為實例化 Handler 對象時 , 會獲取當前線程的 Looper , 如果為空 , 直接拋異常 ;
② Looper 對象創(chuàng)建 : Looper.prepare() 的作用就是創(chuàng)建 Looper 對象 , 將其放入 ThreadLocal 對象中存儲 , 保證線程有且只有一個 Looper 對象 ;
public class handler {...//獲取 Looper 對象后 , 可以從 Looper 對象中獲取 MessageQueue//關聯(lián)后 , Handler 發(fā)送消息時 , 才能將消息精準的發(fā)送給final MessageQueue mQueue;//Handler 需要與線程的唯一 Looper 對象關聯(lián)final Looper mLooper; ...public Handler() {...mLooper = Looper.myLooper()if (mLooper == null) {throw new RuntimeException("Can't create handler inside thread that has not called Looper.prepare()");}...mQueue = mLooper.mQueue;...}... }5 . 普通子線程 轉為 Looper 子線程 代碼示例 :
package kim.hsl.handler;import android.os.Handler; import android.os.Looper; import android.os.Message;import androidx.annotation.NonNull;/*** 將普通線程轉為 Looper 線程** 1. 定義 Handler 成員 ( 可以定義若干個 )* 2. Looper.prepare()* 3. 實例化 Handler 成員* 4. Looper.loop()** @author hsl*/ public class LooperThread extends Thread {/*** 1. 定義時不要實例化* Handler 實例化需要關聯(lián) Looper 對象* Looper 對象在 Looper*/private Handler handler;@Overridepublic void run() {super.run();//2. 將線程轉為 Looper 線程//主要是創(chuàng)建 Looper 放入 ThreadLocal 對象中Looper.prepare();//3. 創(chuàng)建 Handler 必須在 Looper.prepare() 之后, 否則會崩潰handler = new Handler(){@Overridepublic void handleMessage(@NonNull Message msg) {super.handleMessage(msg);//TODO 處理 Handler 相關業(yè)務邏輯}};//4. Looper 開始輪詢 MessageQueue, 將消息調度給 Handler 處理Looper.loop();}public Handler getHandler() {return handler;}public void setHandler(Handler handler) {this.handler = handler;} }
VI . Handler 發(fā)送 消息 種類
Handler 既可以發(fā)送靜態(tài)的 消息 ( Message ) , 又可以發(fā)送動態(tài)的 操作 ( Runnable ) ;
當 Handler 所在的 Looper 線程接收到 消息 ( Message ) 時 , Looper 輪詢到該 消息 ( Message ) 后 , 執(zhí)行該消息對應的業(yè)務邏輯 , 這些邏輯一般都是在 Handler 中提前定義好的 ;
當 Handler 所在的 Looper 線程接收到 操作 ( Runnable ) 時 , Looper 輪詢到該 操作 ( Runnable ) 后 , 直接運行該 Runnable 中的 run() 方法 ;
VII . Handler 機制總結
1 . Handler 機制運行流程 : Message 通過 Handler 發(fā)送給 Looper 線程的 MessageQueue , Looper 輪詢 MessageQueue 將 Message 交給 Handler 執(zhí)行 ;
2 . 線程異步調用 ( 作用 ) : 子線程刷新主線程 UI ( 子線程調用主線程 ) , 主線程調用子線程異步網絡操作 ( 主線程調用子線程 ) ;
3 . 子線程轉 Looper 線程 : ① 定義Handler 成員 ( 可以多個 ) , ② Loop.prepare() , ③ Handler 初始化 ( 必須在 prepare 之后 ) , ④ Looper.loop() ;
4 . 四組件關系 : 111線程 ?\Leftrightarrow? 111 Looper ?\Leftrightarrow? 111 MessageQueue ?\Leftrightarrow? nnn Handler ?\Leftrightarrow? mmm Message ;
總結
以上是生活随笔為你收集整理的【Android】Handler 机制 ( Handler | Message | Looper | MessageQueue )的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【Netty】NIO 网络通信 Sele
- 下一篇: 【Netty】NIO 网络编程 聊天室案