1. 程式人生 > >【android】HandlerThread的使用及原始碼剖析

【android】HandlerThread的使用及原始碼剖析

有時候我們需要在應用程式中建立一些常駐的子執行緒不定期地執行一些計算型任務,這時候可以考慮使用HandlerThread,它具有建立帶訊息迴圈的子執行緒的作用。 一、HanderThread使用示例 先熟悉下HandlerThread的一般用法。我們建立一個如下所示的Activity:
  1. package com.example.handlethreaddemo;  
  2. import android.app.Activity;  
  3. import android.os.Bundle;  
  4. import android.os.Handler;  
  5. import android.os.HandlerThread;  
  6. import android.os.Looper;  
  7. import android.os.Message;  
  8. import android.util.Log;  
  9. publicclass MainActivity extends Activity  
  10. {  
  11.     private Looper mLooper;  
  12.     private MyHandler mHandler;  
  13.     privatestaticfinal String TAG = "MainActivity";  
  14.     privatestaticclass MyHandler extends Handler  
  15.     {  
  16.         public MyHandler(Looper looper)  
  17.         {  
  18.             super(looper);  
  19.         }  
  20.         @Override
  21.         publicvoid handleMessage(Message msg)  
  22.         {  
  23.             switch (msg.what)  
  24.             {  
  25.             case1:  
  26.                 Log.i(TAG, "當前執行緒是"+Thread.currentThread().getName()+
    ",TEST 1");  
  27.                 break;  
  28.             case2:  
  29.                 Log.i(TAG, "TEST 2");  
  30.                 break;  
  31.             }  
  32.         }  
  33.     }  
  34.     @Override
  35.     protectedvoid onCreate(Bundle savedInstanceState)  
  36.     {  
  37.         super.onCreate(savedInstanceState);  
  38.         setContentView(R.layout.activity_main);  
  39.         //建立HandlerThread物件
  40.         HandlerThread myHandleThread = new HandlerThread("HandlerThread<子執行緒>");  
  41.         //啟動HandlerThread---->內部將啟動訊息迴圈
  42.         myHandleThread.start();  
  43.         //獲取Looper
  44.         mLooper = myHandleThread.getLooper();  
  45.         //構造Handler,傳入子執行緒中的Looper
  46.         mHandler = new MyHandler(mLooper);  
  47.         /* 
  48.          * 注:經過上述步驟,Handler將繫結子執行緒的Looper和MessageQueue. 
  49.          * 也就是說handleMessage最終由子執行緒呼叫 
  50.          * */
  51.         mHandler.sendEmptyMessage(1);  
  52.         Log.i(TAG,"當前執行緒是:"+Thread.currentThread().getName());  
  53.     }  
  54. }  

使用HandlerThread內部提供的Looper物件構造Handler物件,然後在ui執行緒中向Handler傳送訊息。log日誌如下:


可見傳送訊息的執行緒為UI執行緒,而處理訊息的執行緒為子執行緒,也就是說,我們在子執行緒中建立了訊息迴圈。 一般情況下,我們總是在UI執行緒中建立Handler物件,並使用介面元件提供的預設Looper,這個Looper繫結在UI執行緒上。所以我們線上程中向Handler傳送訊息時,最終的處理是在主執行緒中進行的。但正如開篇所說,我們有時需要構建常駐的子執行緒以不定期執行計算型任務,這時在子執行緒中建立訊息迴圈將非常有用。 二、HandlerThread原始碼剖析 HandlerThread原始碼十分精簡。 HandlerThread繼承自java.lang.Thread,並封裝了Looper物件:
  1. int mPriority;//優先順序
  2.  int mTid = -1;//執行緒標誌
  3.  Looper mLooper;//訊息迴圈

可通過構造器注入執行緒優先順序,預設優先順序為Process.THREAD_PRIORITY_DEFAULT
  1. public HandlerThread(String name, int priority) {  
  2.        super(name);  
  3.        mPriority = priority;  
  4.    }  

核心邏輯為run方法(複寫Thread類的run方法):
  1. publicvoid run() {  
  2.        mTid = Process.myTid();  
  3.        Looper.prepare();//建立Looper物件
  4.        synchronized (this) {  
  5.            mLooper = Looper.myLooper();//獲取與本執行緒繫結的Looper
  6.            notifyAll();  
  7.        }  
  8.        Process.setThreadPriority(mPriority);  
  9.        onLooperPrepared();//回撥介面,預設為空實現。
  10.        Looper.loop();//啟動訊息迴圈--->may be blocked
  11.        mTid = -1;  
  12.    }  

外界可通過getLooper方法獲取Looper物件:
  1. public Looper getLooper() {  
  2.        if (!isAlive()) {//執行緒死亡
  3.            returnnull;  
  4.        }  
  5.        // If the thread has been started, wait until the looper has been created.
  6.        synchronized (this) {  
  7.            while (isAlive() && mLooper == null) {  
  8.                try {  
  9.                    wait();//非同步等待Looper準備好
  10.                } catch (InterruptedException e) {  
  11.                }  
  12.            }  
  13.        }  
  14.        return mLooper;  
  15.    }  

如果呼叫getLooper方法時,Looper未準備好,那麼將會阻塞執行緒,直到準備好Looper物件。 外界可呼叫quit方法終止訊息迴圈:
  1. publicboolean quit() {  
  2.        Looper looper = getLooper();  
  3.        if (looper != null) {  
  4.            looper.quit();//內部呼叫looper類的quit
  5.            returntrue;  
  6.        }  
  7.        returnfalse;  
  8.    }  

附: Looper類相信大家都不陌生,這裡順便簡單提下(之前寫過Handler和Looper): Looper.prepare方法將會建立一個Looper物件(Looper類的構造器為私有,不可new),並將其放到ThreadLocal中,意為執行緒區域性變數:
  1. publicstaticvoid prepare() {  
  2.         prepare(true);  
  3.     }  
  4.     privatestaticvoid prepare(boolean quitAllowed) {  
  5.         if (sThreadLocal.get() != null) {  
  6.             thrownew RuntimeException("Only one Looper may be created per thread");  
  7.         }  
  8.         sThreadLocal.set(new Looper(quitAllowed));  
  9.     }  

然後通過Looper.myLooper方法返回與本執行緒繫結的Looper,正是剛建立的Looper:
  1. publicstatic Looper myLooper() {  
  2.         return sThreadLocal.get();  
  3.     }  

Looper.loop方法將啟動訊息迴圈,不斷從其內部封裝的訊息佇列MessageQueue中取出訊息,交由Handler執行。
  1. publicstaticvoid loop() {  
  2.         final Looper me = myLooper();  
  3.         if (me == null) {  
  4.             thrownew RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");  
  5.         }  
  6.         final MessageQueue queue = me.mQueue;  
  7.         // Make sure the identity of this thread is that of the local process,
  8.         // and keep track of what that identity token actually is.
  9.         Binder.clearCallingIdentity();  
  10.         finallong ident = Binder.clearCallingIdentity();  
  11.         for (;;) {  
  12. 相關推薦

    androidHandlerThread的使用原始碼剖析

    有時候我們需要在應用程式中建立一些常駐的子執行緒不定期地執行一些計算型任務,這時候可以考慮使用HandlerThread,它具有建立帶訊息迴圈的子執行緒的作用。 一、HanderThread使用示例 先熟悉下HandlerThread的一般用法。我們建立一個如下所示的

    android仿360手機衛士的簡易設計思路原始碼

             筆者最近一直忙於滿廣州的跑,實習好難找好難找,部落格也是有點久沒去更新。仿360手機衛士的實現的目的更多的是出於對常用知識點的一個鞏固吧,比較適合像我這種接觸沒多久的學習者在學習之餘拿來練手保持寫程式碼的感覺的的一個不錯的小專案。 涉及的技術: 都是

    AndroidRetrofit原始碼分析

    Retrofit簡介 retrofit n. 式樣翻新,花樣翻新 vt. 給機器裝置裝配(新部件),翻新,改型 Retrofit 是一個 RESTful 的 HTTP 網路請求框架的封裝。注意這裡並沒有說它是網路請求框架,主要原因在於網路請求的工作並不是 Retrofit

    AndroidOkHttp原始碼分析

    Android為我們提供了兩種HTTP互動的方式:HttpURLConnection 和 Apache HttpClient,雖然兩者都支援HTTPS,流的上傳和下載,配置超時,IPv6和連線池,已足夠滿足我們各種HTTP請求的需求。但更高效的使用HTTP 可以讓您的應用執行更快、更節省

    Android原始碼分析 - LRUCache快取實現原理

    一、Android中的快取策略 一般來說,快取策略主要包含快取的新增、獲取和刪除這三類操作。如何新增和獲取快取這個比較好理解,那麼為什麼還要刪除快取呢?這是因為不管是記憶體快取還是硬碟快取,它們的快取大小都是有限的。當快取滿了之後,再想其新增快取,這個時候就需要刪除一些舊的快取

    Android原始碼分析 - View事件分發機制

    事件分發物件 (1)所有 Touch 事件都被封裝成了 MotionEvent 物件,包括 Touch 的位置、時間、歷史記錄以及第幾個手指(多指觸控)等。 (2)事件型別分為 ACTION_DOWN, ACTION_UP,ACTION_MOVE,ACTION_POINTER_D

    Android原始碼分析 - Activity啟動流程

    啟動Activity的方式 Activity有2種啟動的方式,一種是在Launcher介面點選應用的圖示、另一種是在應用中通過Intent進行跳轉。我們主要介紹與後者相關的啟動流程。 Intent intent = new Intent(this, TestActivity

    android安卓的許可權提示版本相關

    Only dangerous permissions require user agreement. The way Android asks the user to grant dangerous permissions depends on the version of Android running o

    MapReduce詳解原始碼解析(一)——分片輸入、MapperMap端Shuffle過程

    title: 【MapReduce詳解及原始碼解析(一)】——分片輸入、Mapper及Map端Shuffle過程 date: 2018-12-03 21:12:42 tags: Hadoop categories: 大資料 toc: true 點選檢視我的部落格:Josonlee’

    Android一鍵清理後臺實現原始碼

    整個程式很簡單 只有一個CleanActivity,實現清理後臺及並展示動畫效果 一個桌面小部件,點選啟動CleanActivity,實現同樣效果 一.  CleanActivity的實現 1.  主程式佈局 對應佈局為檔案為 R.layout.activity_clea

    AndroidAndroid開源專案(一)音樂播放器原始碼彙總

    作為一個有追求的程式設計師來說,專案原始碼必須看,但是網上那麼多資源是不讓你無從下手啊,博主今天為大家推薦五個經典專案吧。 一、android-UniversalMusicPlayer 這個開源專案展示瞭如何實現一個橫跨各種Android平臺的音樂播放器,包

    Linux 1.0核心原始碼剖析執行程式——exec.c

    父程序 fork的子程序的目的自然不是建立一個幾乎與自己一模一樣的程序。而是通過子程序呼叫 exec 函式簇去執行另外一個程式。exec() 系統呼叫必須定位該執行檔案的二進位制映像,載入並執行它。 exec() 的Linux實現支援不同的二進位制格式,這是通過 linux

    androidsdk安裝環境變量配置、android stdio的安裝新建項目

    工程 end tps 發現 tool 版本 不知道 php work (前提,安裝配置好jdk) 首先下載一個sdk(http://tools.android-studio.org/index.php/sdk) 我先下的是.exe版本,因為後面括號Recommend,

    Android原始碼解析View.post()

    emmm,大夥都知道,子執行緒是不能進行 UI 操作的,或者很多場景下,一些操作需要延遲執行,這些都可以通過 Handler 來解決。但說實話,實在是太懶了,總感覺寫 Handler 太麻煩了,一不小心又很容易寫出記憶體洩漏的程式碼來,所以為了偷懶,我就經常用 View.

    AndroidAndroid Camera實時資料採集通過MediaCodec硬編碼編碼資料的流程

    // video device. private Camera camera; private MediaCodec vencoder; private MediaCodecInfo vmci; private MediaCodec.BufferInfo vebi; private byte[] vbuff

    Android對EditText輸入金額新增限制監聽

    場景分析: 專案中我們需要在一個EditText控制元件中輸入金額,限制只能輸入數字,可以為小數但保留2位小數,同時監聽EditText一旦發現輸入金額大於0那麼使介面上的提交按鈕可點。 第一步:設定EditText的xml屬性 <EditText andr

    android自定義ProgressDialog實現暫時隱藏進度值並顯示等待狀態(附原始碼下載)

    有時,我們需要訪問網路才能獲取到需要操作的任務數(例如下載的檔案數),而在伺服器返回任務數之前要想隱藏進度百分比和進度數值,就需要我們自己重寫ProgressDialog。等到獲取到任務數後再把進度值和百分比顯示出來。先上效果圖: 關鍵程式碼: public clas

    Android原始碼分析

    一、Android中的快取策略 一般來說,快取策略主要包含快取的新增、獲取和刪除這三類操作。如何新增和獲取快取這個比較好理解,那麼為什麼還要刪除快取呢?這是因為不管是記憶體快取還是硬碟快取,它們的快取大小都是有限的。當快取滿了之後,再想其新增快取,這個時候

    AndroidAndroid常見的錯誤解決

    1.ActivityManager: Warning: Activity not started, its current task has been brought to the front  說明:模擬器中已經有一個例項在執行。  解決方法:退出模擬器中執行的程式,再次

    AndroidAndroidStudio上傳程式碼到SVN從SVN中檢出程式碼

    最近開發新專案,需要進行版本控制,以前沒有在AndroidStudio使用過SVN,查找了一些資料,現在簡單整理一下,做一個備忘,也希望讓讀者朋友們少走一點彎路. 首先記錄一下將程式碼上傳到SVN伺服器倉庫,步驟如下: 一. 選擇忽略檔案 按