日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

android handler封装_Handler都没搞懂,你拿什么去跳槽啊?!

發布時間:2023/12/20 编程问答 52 豆豆
生活随笔 收集整理的這篇文章主要介紹了 android handler封装_Handler都没搞懂,你拿什么去跳槽啊?! 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

0. 前言

做 Android 開發肯定離不開跟 Handler 打交道,它通常被我們用來做主線程與子線程之間的通信工具,而 Handler 作為 Android 中消息機制的重要一員也確實給我們的開發帶來了極大的便利。

Handler應用之廣泛,可以說只要有異步線程與主線程通信的地方就一定會有 Handler

所以搞懂 Handler 對理解Android非常有必要。

那么,Handler 的通信機制的背后的原理是什么?

本文帶你揭曉。

注意:本文所展示的系統源碼基于 Android-27 ,并有所刪減。

1. 重識 Handler

我們可以使用 Handler 發送并處理與一個線程關聯的 Message 和 Runnable 。(注意:Runnable 會被封裝進一個 Message,所以它本質上還是一個 Message )

每個 Handler 都會跟一個線程綁定,并與該線程的 MessageQueue 關聯在一起,從而實現消息的管理以及線程間通信。

1.1 Handler 的基本用法

1android.os.Handler handler = new Handler(){2 @Override3 public void handleMessage(final Message msg) {4 //這里接受并處理消息5 }6};7//發送消息8handler.sendMessage(message);9handler.post(runnable);

實例化一個 Handler 重寫 handleMessage 方法 ,然后在需要的時候調用它的 send 以及 post 系列方法就可以了,非常簡單易用,并且支持延時消息。(更多方法可查詢 API 文檔)

但是奇怪,我們并沒有看到任何 MessageQueue 的身影,也沒看到它與線程綁定的邏輯,這是怎么回事

2. Handler 原理解析

相信大家早就聽說過了 Looper 以及 MessageQueue 了,我就不多繞彎子了。

不過在開始分析原理之前,先明確我們的問題

  • Handler 是如何與線程關聯的?
  • Handler 發出去的消息是誰管理的?
  • 消息又是怎么回到 handleMessage() 方法的?
  • 線程的切換是怎么回事?
  • 2.1 Handler 與 Looper 的關聯

    實際上我們在實例化 Handler 的時候 Handler 會去檢查當前線程的 Looper 是否存在,如果不存在則會報異常,也就是說在創建 Handler 之前一定需要先創建 Looper

    代碼如下:

    1public Handler(Callback callback, boolean async) { 2 //檢查當前的線程是否有 Looper 3 mLooper = Looper.myLooper(); 4 if (mLooper == null) { 5 throw new RuntimeException( 6 "Can't create handler inside thread that has not called Looper.prepare()"); 7 } 8 //Looper 持有一個 MessageQueue 9 mQueue = mLooper.mQueue;10}

    這個異常相信很多同學遇到過,而我們平時直接使用感受不到這個異常是因為主線程已經為我們創建好了 Looper,先記住,后面會講。(見【3.2】)

    一個完整的 Handler 使用例子其實是這樣的:

    1class LooperThread extends Thread { 2 public Handler mHandler; 3 public void run() { 4 Looper.prepare(); 5 mHandler = new Handler() { 6 public void handleMessage(Message msg) { 7 // process incoming messages here 8 } 9 };10 Looper.loop();11 }12}

    Looper.prepare() :

    1//Looper2private static void prepare(boolean quitAllowed) {3 if (sThreadLocal.get() != null) {4 throw new RuntimeException("Only one Looper may be created per thread");5 }6 sThreadLocal.set(new Looper(quitAllowed));7}

    Looper 提供了 Looper.prepare() 方法來創建 Looper ,并且會借助 ThreadLocal 來實現與當前線程的綁定功能。Looper.loop() 則會開始不斷嘗試從 MessageQueue 中獲取 Message , 并分發給對應的 Handler(見【2.3】)

    也就是說 Handler 跟線程的關聯是靠 Looper 來實現的。

    2.2 Message 的存儲與管理

    Handler 提供了一些列的方法讓我們來發送消息,如 send()系列 post()系列 。

    不過不管我們調用什么方法,最終都會走到 Message.enqueueMessage(Message,long) 方法。

    以 sendEmptyMessage(int) 方法為例:

    1//Handler2sendEmptyMessage(int)3 -> sendEmptyMessageDelayed(int,int)4 -> sendMessageAtTime(Message,long)5 -> enqueueMessage(MessageQueue,Message,long)6 -> queue.enqueueMessage(Message, long);

    到了這里,消息的管理者 MessageQueue 也就露出了水面

    MessageQueue 顧明思議,就是個隊列,負責消息的入隊出隊。

    2.3 Message 的分發與處理

    了解清楚 Message 的發送與存儲管理后,就該揭開分發與處理的面紗了。

    前面說到了 Looper.loop() 負責對消息的分發,本章節進行分析。

    先來看看所涉及到的方法:

    1//Looper 2public static void loop() { 3 final Looper me = myLooper(); 4 if (me == null) { 5 throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); 6 } 7 final MessageQueue queue = me.mQueue; 8 //... 9 for (;;) {10 // 不斷從 MessageQueue 獲取 消息11 Message msg = queue.next(); // might block12 //退出 Looper 13 if (msg == null) {14 // No message indicates that the message queue is quitting.15 return;16 }17 //...18 try {19 msg.target.dispatchMessage(msg);20 end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();21 } finally {22 //...23 }24 //...25 //回收 message, 見【3.5】26 msg.recycleUnchecked();27 }28}

    loop() 里調用了 MessageQueue.next() :

    1//MessageQueue 2Message next() { 3 //... 4 for (;;) { 5 //... 6 nativePollOnce(ptr, nextPollTimeoutMillis); 7 8 synchronized (this) { 9 // Try to retrieve the next message. Return if found.10 final long now = SystemClock.uptimeMillis();11 Message prevMsg = null;12 Message msg = mMessages;13 //...14 if (msg != null) {15 if (now < msg.when) {16 // Next message is not ready. Set a timeout to wake up when it is ready.17 nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);18 } else {19 // Got a message.20 mBlocked = false;21 if (prevMsg != null) {22 prevMsg.next = msg.next;23 } else {24 mMessages = msg.next;25 }26 msg.next = null;27 return msg;28 }29 } else {30 // No more messages.31 nextPollTimeoutMillis = -1;32 }3334 // Process the quit message now that all pending messages have been handled.35 if (mQuitting) {36 dispose();37 return null;38 }39 }4041 // Run the idle handlers. 關于 IdleHandler 自行了解42 //...43 }44}

    還調用了 msg.target.dispatchMessage(msg) ,msg.target 就是發送該消息的 Handler,這樣就回調到了 Handler 那邊去了:

    1//Handler 2public void dispatchMessage(Message msg) { 3 //msg.callback 是 Runnable ,如果是 post方法則會走這個 if 4 if (msg.callback != null) { 5 handleCallback(msg); 6 } else { 7 //callback 見【3.4】 8 if (mCallback != null) { 9 if (mCallback.handleMessage(msg)) {10 return;11 }12 }13 //回調到 Handler 的 handleMessage 方法14 handleMessage(msg);15 }16}

    注意:dispatchMessage() 方法針對 Runnable 的方法做了特殊處理,如果是 ,則會直接執行 Runnable.run() 。

    分析:Looper.loop() 是個死循環,會不斷調用 MessageQueue.next() 獲取 Message ,并調用 msg.target.dispatchMessage(msg) 回到了 Handler 來分發消息,以此來完成消息的回調

    注意:loop()方法并不會卡死主線程,見【6】。

    那么線程的切換又是怎么回事呢?

    很多人搞不懂這個原理,但是其實非常簡單,我們將所涉及的方法調用棧畫出來,如下:

    1Thread.foo(){2 Looper.loop()3 -> MessageQueue.next()4 -> Message.target.dispatchMessage()5 -> Handler.handleMessage()6}

    顯而易見,Handler.handleMessage() 所在的線程最終由調用 Looper.loop() 的線程所決定。

    平時我們用的時候從異步線程發送消息到 Handler,這個 Handler 的 handleMessage() 方法是在主線程調用的,所以消息就從異步線程切換到了主線程。

    2.3 圖解原理

    文字版的原理解析到這里就結束了,如果你看到這里還是沒有懂,沒關系,我特意給你們準備了些圖,配合著前面幾個章節,再多看幾遍,一定可以吃透。

    handler-looper-mq.jpg

    handler_java.jpg

    圖片來源見【6】

    2.4 小結

    Handler 的背后有著 Looper 以及 MessageQueue 的協助,三者通力合作,分工明確。

    嘗試小結一下它們的職責,如下:

    • Looper :負責關聯線程以及消息的分發,會與創建它的線程綁定,并負責在該線程下從 MessageQueue 獲取 Message,分發給 Handler ;
    • MessageQueue :是個隊列,負責消息的存儲與管理,負責管理由 Handler 發送過來的 Message ;
    • Handler : 負責發送并處理消息,面向開發者,提供 API,并隱藏背后實現的細節。

    對【2】章節提出的問題用一句話總結:

    Handler 發送的消息由 MessageQueue 存儲管理,并由 Loopler 負責回調消息到 handleMessage()。

    線程的轉換由 Looper 完成,handleMessage() 所在線程由 Looper.loop() 調用者所在線程決定。

    3. Handler 的延伸

    Handler 雖然簡單易用,但是要用好它還是需要注意一點,另外 Handler相關 還有些鮮為人知的知識技巧,比如 IdleHandler。

    由于 Handler 的特性,它在 Android 里的應用非常廣泛,比如: AsyncTask、HandlerThread、Messenger、IdleHandler 和 IntentService 等等。

    這些我會講解一些,我沒講到的可以自行搜索相關內容進行了解。

    3.1 Handler 引起的內存泄露原因以及最佳解決方案

    Handler 允許我們發送延時消息,如果在延時期間用戶關閉了 Activity,那么該 Activity 會泄露。

    這個泄露是因為 Message 會持有 Handler,而又因為 Java 的特性,內部類會持有外部類,使得 Activity 會被 Handler 持有,這樣最終就導致 Activity 泄露。

    解決該問題的最有效的方法是:將 Handler 定義成靜態的內部類,在內部持有 Activity 的弱引用,并及時移除所有消息

    示例代碼如下:

    1private static class SafeHandler extends Handler { 2 3 private WeakReference ref; 4 5 public SafeHandler(HandlerActivity activity) { 6 this.ref = new WeakReference(activity); 7 } 8 9 @Override10 public void handleMessage(final Message msg) {11 HandlerActivity activity = ref.get();12 if (activity != null) {13 activity.handleMessage(msg);14 }15 }16}

    并且再在 Activity.onDestroy() 前移除消息,加一層保障:

    1@Override2protected void onDestroy() {3 safeHandler.removeCallbacksAndMessages(null);4 super.onDestroy();5}

    這樣雙重保障,就能完全避免內存泄露了。

    注意:單純的在 onDestroy 移除消息并不保險,因為 onDestroy 并不一定執行。

    3.2 為什么我們能在主線程直接使用 Handler,而不需要創建 Looper ?

    前面我們提到了每個Handler 的線程都有一個 Looper ,主線程當然也不例外,但是我們不曾準備過主線程的 Looper 而可以直接使用,這是為何?

    注意:通常我們認為 ActivityThread 就是主線程。事實上它并不是一個線程,而是主線程操作的管理者,所以吧,我覺得把 ActivityThread 認為就是主線程無可厚非,另外主線程也可以說成 UI 線程。

    在 ActivityThread.main() 方法中有如下代碼:

    1//android.app.ActivityThread 2public static void main(String[] args) { 3 //... 4 Looper.prepareMainLooper(); 5 6 ActivityThread thread = new ActivityThread(); 7 thread.attach(false); 8 9 if (sMainThreadHandler == null) {10 sMainThreadHandler = thread.getHandler();11 }12 //...13 Looper.loop();1415 throw new RuntimeException("Main thread loop unexpectedly exited");16}

    Looper.prepareMainLooper(); 代碼如下:

    1/** 2 * Initialize the current thread as a looper, marking it as an 3 * application's main looper. The main looper for your application 4 * is created by the Android environment, so you should never need 5 * to call this function yourself. See also: {@link #prepare()} 6 */ 7public static void prepareMainLooper() { 8 prepare(false); 9 synchronized (Looper.class) {10 if (sMainLooper != null) {11 throw new IllegalStateException("The main Looper has already been prepared.");12 }13 sMainLooper = myLooper();14 }15}

    可以看到在 ActivityThread 里 調用了 Looper.prepareMainLooper() 方法創建了 主線程的 Looper ,并且調用了 loop() 方法,所以我們就可以直接使用 Handler 了。

    注意:Looper.loop() 是個死循環,后面的代碼正常情況不會執行。

    3.3 主線程的 Looper 不允許退出

    如果你嘗試退出 Looper ,你會得到以下錯誤信息:

    1Caused by: java.lang.IllegalStateException: Main thread not allowed to quit.2 at android.os.MessageQueue.quit(MessageQueue.java:415)3 at android.os.Looper.quit(Looper.java:240)

    why? 其實原因很簡單,主線程不允許退出,退出就意味 APP 要掛。

    3.4 Handler 里藏著的 Callback 能干什么?

    在 Handler 的構造方法中有幾個 要求傳入 Callback ,那它是什么,又能做什么呢?

    來看看 Handler.dispatchMessage(msg) 方法:

    1public void dispatchMessage(Message msg) { 2 //這里的 callback 是 Runnable 3 if (msg.callback != null) { 4 handleCallback(msg); 5 } else { 6 //如果 callback 處理了該 msg 并且返回 true, 就不會再回調 handleMessage 7 if (mCallback != null) { 8 if (mCallback.handleMessage(msg)) { 9 return;10 }11 }12 handleMessage(msg);13 }14}

    可以看到 Handler.Callback 有優先處理消息的權利 ,當一條消息被 Callback 處理并攔截(返回 true),那么 Handler 的 handleMessage(msg) 方法就不會被調用了;如果 Callback 處理了消息,但是并沒有攔截,那么就意味著一個消息可以同時被 Callback 以及 Handler 處理

    這個就很有意思了,這有什么作用呢?

    我們可以利用 Callback 這個攔截機制來攔截 Handler 的消息!

    場景:Hook ActivityThread.mH , 在 ActivityThread 中有個成員變量 mH ,它是個 Handler,又是個極其重要的類,幾乎所有的插件化框架都使用了這個方法。

    3.5 創建 Message 實例的最佳方式

    由于 Handler 極為常用,所以為了節省開銷,Android 給 Message 設計了回收機制,所以我們在使用的時候盡量復用 Message ,減少內存消耗。

    方法有二:

  • 通過 Message 的靜態方法 Message.obtain(); 獲取;
  • 通過 Handler 的公有方法 handler.obtainMessage(); 。
  • 3.6 子線程里彈 Toast 的正確姿勢

    當我們嘗試在子線程里直接去彈 Toast 的時候,會 crash :

    1java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare() 2

    本質上是因為 Toast 的實現依賴于 Handler,按子線程使用 Handler 的要求修改即可(見【2.1】),同理的還有 Dialog。

    正確示例代碼如下:

    1new Thread(new Runnable() {2 @Override3 public void run() {4 Looper.prepare();5 Toast.makeText(HandlerActivity.this, "不會崩潰啦!

    總結

    以上是生活随笔為你收集整理的android handler封装_Handler都没搞懂,你拿什么去跳槽啊?!的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。