1. 程式人生 > >Android非同步訊息處理機制 handler

Android非同步訊息處理機制 handler

我們都知道,Android UI是執行緒不安全的,如果在子執行緒中嘗試進行UI操作,程式就有可能會崩潰。相信大家在日常的工作當中都會經常遇到這個問題,解決的方案應該也是早已爛熟於心,即建立一個Message物件,然後藉助Handler傳送出去,之後在Handler的handleMessage()方法中獲得剛才傳送的Message物件,然後在這裡進行UI操作就不會再出現崩潰了。
這種處理方式被稱為非同步訊息處理執行緒,雖然我相信大家都會用,可是你知道它背後的原理是什麼樣的嗎?今天我們就來一起深入探究一下Handler和Message背後的祕密。

首先來看一下如何建立Handler物件。你可能會覺得挺納悶的,建立Handler有什麼好看的呢,直接new一下不就行了?確實,不過即使只是簡單new一下,還是有不少地方需要注意的,我們嘗試在程式中建立兩個Handler物件,一個在主執行緒中建立,一個在子執行緒中建立,程式碼如下所示:

  1. publicclass MainActivity extends Activity {  
  2.     private Handler handler1;  
  3.     private Handler handler2;  
  4.     @Override
  5.     protectedvoid onCreate(Bundle savedInstanceState) {  
  6.         super.onCreate(savedInstanceState);  
  7.         setContentView(R.layout.activity_main);  
  8.         handler1 = new
     Handler();  
  9.         new Thread(new Runnable() {  
  10.             @Override
  11.             publicvoid run() {  
  12.                 handler2 = new Handler();  
  13.             }  
  14.         }).start();  
  15.     }  
  16. }  
如果現在執行一下程式,你會發現,在子執行緒中建立的Handler是會導致程式崩潰的,提示的錯誤資訊為 Can't create handler inside thread that has not called Looper.prepare() 。說是不能在沒有呼叫Looper.prepare() 的執行緒中建立Handler,那我們嘗試在子執行緒中先呼叫一下Looper.prepare()呢,程式碼如下所示:
  1. new Thread(new Runnable() {  
  2.     @Override
  3.     publicvoid run() {  
  4.         Looper.prepare();  
  5.         handler2 = new Handler();  
  6.     }  
  7. }).start();  
果然這樣就不會崩潰了,不過只滿足於此顯然是不夠的,我們來看下Handler的原始碼,搞清楚為什麼不呼叫Looper.prepare()就不行呢。Handler的無參建構函式如下所示:
  1. public Handler() {  
  2.     if (FIND_POTENTIAL_LEAKS) {  
  3.         final Class<? extends Handler> klass = getClass();  
  4.         if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&  
  5.                 (klass.getModifiers() & Modifier.STATIC) == 0) {  
  6.             Log.w(TAG, "The following Handler class should be static or leaks might occur: " +  
  7.                 klass.getCanonicalName());  
  8.         }  
  9.     }  
  10.     mLooper = Looper.myLooper();  
  11.     if (mLooper == null) {  
  12.         thrownew RuntimeException(  
  13.             "Can't create handler inside thread that has not called Looper.prepare()");  
  14.     }  
  15.     mQueue = mLooper.mQueue;  
  16.     mCallback = null;  
  17. }  
可以看到,在第10行呼叫了Looper.myLooper()方法獲取了一個Looper物件,如果Looper物件為空,則會丟擲一個執行時異常,提示的錯誤正是 Can't create handler inside thread that has not called Looper.prepare()!那什麼時候Looper物件才可能為空呢?這就要看看Looper.myLooper()中的程式碼了,如下所示:
  1. publicstaticfinal Looper myLooper() {  
  2.     return (Looper)sThreadLocal.get();  
  3. }  
這個方法非常簡單,就是從sThreadLocal物件中取出Looper。如果sThreadLocal中有Looper存在就返回Looper,如果沒有Looper存在自然就返回空了。因此你可以想象得到是在哪裡給sThreadLocal設定Looper了吧,當然是Looper.prepare()方法!我們來看下它的原始碼:
  1. publicstaticfinalvoid prepare() {  
  2.     if (sThreadLocal.get() != null) {  
  3.         thrownew RuntimeException("Only one Looper may be created per thread");  
  4.     }  
  5.     sThreadLocal.set(new Looper());  
  6. }  

可以看到,首先判斷sThreadLocal中是否已經存在Looper了,如果還沒有則建立一個新的Looper設定進去。這樣也就完全解釋了為什麼我們要先呼叫Looper.prepare()方法,才能建立Handler物件。同時也可以看出每個執行緒中最多隻會有一個Looper物件。

咦?不對呀!主執行緒中的Handler也沒有呼叫Looper.prepare()方法,為什麼就沒有崩潰呢?細心的朋友我相信都已經發現了這一點,這是由於在程式啟動的時候,系統已經幫我們自動呼叫了Looper.prepare()方法。檢視ActivityThread中的main()方法,程式碼如下所示:

  1. publicstaticvoid main(String[] args) {  
  2.     SamplingProfilerIntegration.start();  
  3.     CloseGuard.setEnabled(false);  
  4.     Environment.initForCurrentUser();  
  5.     EventLogger.setReporter(new EventLoggingReporter());  
  6.     Process.setArgV0("<pre-initialized>");  
  7.     Looper.prepareMainLooper();  
  8.     ActivityThread thread = new ActivityThread();  
  9.     thread.attach(false);  
  10.     if (sMainThreadHandler == null) {  
  11.         sMainThreadHandler = thread.getHandler();  
  12.     }  
  13.     AsyncTask.init();  
  14.     if (false) {  
  15.         Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread"));  
  16.     }  
  17.     Looper.loop();  
  18.     thrownew RuntimeException("Main thread loop unexpectedly exited");  
  19. }  
可以看到,在第7行呼叫了Looper.prepareMainLooper()方法,而這個方法又會再去呼叫Looper.prepare()方法,程式碼如下所示:
  1. publicstaticfinalvoid prepareMainLooper() {  
  2.     prepare();  
  3.     setMainLooper(myLooper());  
  4.     if (Process.supportsProcesses()) {  
  5.         myLooper().mQueue.mQuitAllowed = false;  
  6.     }  
  7. }  

因此我們應用程式的主執行緒中會始終存在一個Looper物件,從而不需要再手動去呼叫Looper.prepare()方法了。

這樣基本就將Handler的建立過程完全搞明白了,總結一下就是在主執行緒中可以直接建立Handler物件,而在子執行緒中需要先呼叫Looper.prepare()才能建立Handler物件。

看完了如何建立Handler之後,接下來我們看一下如何傳送訊息,這個流程相信大家也已經非常熟悉了,new出一個Message物件,然後可以使用setData()方法或arg引數等方式為訊息攜帶一些資料,再借助Handler將訊息傳送出去就可以了,示例程式碼如下:

  1. new Thread(new Runnable() {  
  2.     @Override
  3.     publicvoid run() {  
  4.         Message message = new Message();  
  5.         message.arg1 = 1;  
  6.         Bundle bundle = new Bundle();  
  7.         bundle.putString("data""data");  
  8.         message.setData(bundle);  
  9.         handler.sendMessage(message);  
  10.     }  
  11. }).start();  

可是這裡Handler到底是把Message傳送到哪裡去了呢?為什麼之後又可以在Handler的handleMessage()方法中重新得到這條Message呢?看來又需要通過閱讀原始碼才能解除我們心中的疑惑了,Handler中提供了很多個傳送訊息的方法,其中除了sendMessageAtFrontOfQueue()方法之外,其它的傳送訊息方法最終都會輾轉呼叫到sendMessageAtTime()方法中,這個方法的原始碼如下所示:

  1. publicboolean sendMessageAtTime(Message msg, long uptimeMillis)  
  2. {  
  3.     boolean sent = false;  
  4.     MessageQueue queue = mQueue;  
  5.     if (queue != null) {  
  6.         msg.target = this;  
  7.         sent = queue.enqueueMessage(msg, uptimeMillis);  
  8.     }  
  9.     else {  
  10.         RuntimeException e = new RuntimeException(  
  11.             this + " sendMessageAtTime() called with no mQueue");  
  12.         Log.w("Looper", e.getMessage(), e);  
  13.     }  
  14.     return sent;  
  15. }  
sendMessageAtTime()方法接收兩個引數,其中msg引數就是我們傳送的Message物件,而uptimeMillis引數則表示傳送訊息的時間,它的值等於自系統開機到當前時間的毫秒數再加上延遲時間,如果你呼叫的不是sendMessageDelayed()方法,延遲時間就為0,然後將這兩個引數都傳遞到MessageQueue的enqueueMessage()方法中。這個MessageQueue又是什麼東西呢?其實從名字上就可以看出了,它是一個訊息佇列,用於將所有收到的訊息以佇列的形式進行排列,並提供入隊和出隊的方法。這個類是在Looper的建構函式中建立的,因此一個Looper也就對應了一個MessageQueue。

那麼enqueueMessage()方法毫無疑問就是入隊的方法了,我們來看下這個方法的原始碼:

  1. finalboolean enqueueMessage(Message msg, long when) {  
  2.     if (msg.when != 0) {  
  3.         thrownew AndroidRuntimeException(msg + " This message is already in use.");  
  4.     }  
  5.     if (msg.target == null && !mQuitAllowed) {  
  6. 相關推薦

    Android非同步訊息處理機制 handler

    我們都知道,Android UI是執行緒不安全的,如果在子執行緒中嘗試進行UI操作,程式就有可能會崩潰。相信大家在日常的工作當中都會經常遇到這個問題,解決的方案應該也是早已爛熟於心,即建立一個Message物件,然後藉助Handler傳送出去,之後在Handler的han

    Android非同步訊息處理機制Handler

    很多人第一次接觸Handler可能是因為一句話”子執行緒不能操作ui”,那子執行緒能不能操作ui呢?我們在這裡不多討論(其實是可以的,但是執行緒不安全),我們來分析下handler是如何運轉起來的。 一般用法都是在“主執行緒”中new一個handler

    Android非同步訊息處理機制:Looper、Handler、Message

    1 簡介 Handler,Looper,Message這三者都與Android非同步訊息處理執行緒相關, Looper:負責建立一個MessageQueue,然後進入一個無限迴圈體不斷從該MessageQueue中讀取訊息; Handler:訊息建立者,一個或者多個

    (轉載)Android 非同步訊息處理機制 讓你深入理解 Looper、Handler、Message三者關係

    很多人面試肯定都被問到過,請問Android中的Looper , Handler , Message有什麼關係?本篇部落格目的首先為大家從原始碼角度介紹3者關係,然後給出一個容易記憶的結論。 1、 概述 Handler 、 Looper 、Message

    Android 非同步訊息處理機制 讓你深入理解 Looper、Handler、Message三者關係

    很多人面試肯定都被問到過,請問Android中的Looper , Handler , Message有什麼關係?本篇部落格目的首先為大家從原始碼角度介紹3者關係,然後給出一個容易記憶的結論。1、 概述Handler 、 Looper 、Message 這三者都與Android

    android 非同步訊息處理機制 — AHandler

    1. 引入 ALooper、AHandler、AMessage 在 android multimedia stagefright 的框架程式碼中,通篇都是這幾個類的身影,所以熟悉 android 多媒體框架的第一步必須理解這幾個類的含義。 這幾個類是為了實現非同步訊息機制而設計的

    Android非同步訊息處理機制詳解及原始碼分析

    PS一句:最終還是選擇CSDN來整理髮表這幾年的知識點,該文章平行遷移到CSDN。因為CSDN也支援MarkDown語法了,牛逼啊! 【工匠若水 http://blog.csdn.net/yanbober 轉載煩請註明出處,尊重分享成果】 最近相對來說比較閒,加上養病,所

    淺談Android訊息處理機制--Handler

    1.為什麼有Handler? 在UI執行緒中不能進行耗時操作,例如資料讀寫、網路請求、圖片載入等,所以這些操作被放在子執行緒裡,Handler便是子執行緒和UI執行緒之間通訊的橋樑之一。 2.幹什麼用的? 進行非同步訊息處理,即上述內容。 3.Handler類裡面有什麼是必須知道

    深入理解Android非同步訊息處理機制

    一。概述   Android 中的非同步訊息處理主要分為四個部分組成,Message、Hndler、MessageQueue 和 Looper。其關係如下圖所示:     1. Message 是執行緒之間傳遞的訊息,它可以在內部攜帶少量資訊,用於在不同執行緒之間交換資料。   2. Messag

    android非同步訊息處理機制

     android非同步訊息處理主要由四部分組成:Handler,Looper,Message,MessageQueue​ Message:執行緒之間傳遞的訊息,可以在內部攜帶少量訊息 MessageQueue: Looper:每個執行緒有且最多隻能有一個Looper物件

    Android非同步訊息處理機制的原始碼分析

    1、背景 相信做過一段時間的Android開發都瞭解,當我們在子執行緒中更新UI時會丟擲異常,導致程式崩潰,Android4.0之後只允許在UI執行緒中更新介面,但是我們也不能再UI執行緒中處理耗時操作,那樣會導致應用程式無響應(即出現ANR)。 那如果想解

    Android非同步訊息處理機制原始碼分析

    宣告:本文是參考了以下幾位大神的文章,自己按照自己的思維習慣整理的筆記,並新增一些相關的內容。如有不正確的地方歡迎留言指出,謝謝! 郭霖部落格 鴻洋部落格 任玉剛《Android開發藝術探索》 一. Andoid訊息機制概述

    Android 非同步訊息處理機制解析

    一、Message、Handler、MessageQueue、Looper   Android 中的非同步訊息處理主要由四個部分組成,Message、Handler、MessageQueue、Looper。   1. Message: Message 是線上

    Android非同步訊息處理機制學習筆記

    (一)Handler 什麼是Handler Android訊息機制的上層介面,Handler通過傳送和處理Message和Runnable物件來關聯相對應執行緒的MessageQueeu. 可

    Android非同步訊息處理機制詳解

    關於Handler例項化的一些關鍵資訊,具體如下: 在主執行緒中可以直接建立Handler物件,而在子執行緒中需要先呼叫Looper.prepare()才能建立Handler物件,否則執行丟擲”

    Android Handler 非同步訊息處理機制的妙用 建立強大的圖片載入類

                    最近建立了一個群,方便大家交流,群號:55032675上一篇部落格介紹了Android非同步訊息處理機制,如果你還不瞭解,可以看:Android 非同步訊息處理機制 讓你深入理解 Looper、Handler、Message三者關係 。那篇部落格的最後,提出可以把非同步訊息處理

    Android Handler 非同步訊息處理機制三:妙用手法 建立強大的圖片載入類

    上一篇部落格介紹了Android非同步訊息處理機制,如果你還不瞭解,可以看:Android 非同步訊息處理機制 讓你深入理解 Looper、Handler、Message三者關係 。那篇部落格的最後,提出可以把非同步訊息處理機制不僅僅是在MainActivit

    android非同步訊息處理機制Handler

    這時就需要Handler了,修改MainActivity如下:public class MainActivity extends AppCompatActivity { public static final int UPDATE_TEXT =1 ; @Override protec

    Android Handler(子執行緒Handler非同步訊息處理機制的妙用(活用訊號量) 建立強大的圖片載入類

    最近建立了一個群,方便大家交流,群號:55032675 上一篇部落格介紹了Android非同步訊息處理機制,如果你還不瞭解,可以看:Android 非同步訊息處理機制 讓你深入理解 Looper、Handler、Message三者關係 。那篇部落格的最後