概述
詳細
原文地址:
一、准備工作
開發環境:
jdk1.8
Eclipse Luna Service Release 1 (4.4.1)
運行環境:
華為榮耀6(Android4.4)、華為p9(Android7.0)
實現功能:
Android HandlerThread的使用
二、程序實現
1、需要截圖程序結構
HandlerThread類介紹
Handy class for starting a new thread that has a looper. The looper can then be used to create handler classes. Note that start() must still be called.
HandlerThread是Android API提供的一個方便、便捷的類,使用它我們可以快速的創建一個帶有Looper的線程。Looper可以用來創建Handler實例。注意:start()仍然必須被調用。
如下是HandlerThread使用的demo。
package com.zpengyong.hand; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; import android.os.Message; import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.TextView; public class MainActivity extends Activity { private final static String TAG = "MainActivity"; private Button mGet; private TextView mResult; protected final int MSG_GET = 1; protected final int MSG_RESULT = 2; private HandlerThread mHandlerThread; //子線程中的Handler實例。 private Handler mSubThreadHandler; //與Ui線程綁定的Handler實例。 private Handler mUiHandler = new Handler(){ public void handleMessage(Message msg) { Log.i(TAG, "mUiHandler handleMessage thread:"+Thread.currentThread()); switch (msg.what) { case MSG_RESULT: mResult.setText((String)msg.obj); break; default: break; } }; }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Log.i(TAG, "onCreate thread:"+Thread.currentThread()); mGet = (Button) findViewById(R.id.get); mGet.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { mSubThreadHandler.sendEmptyMessage(MSG_GET); } }); mResult = (TextView) findViewById(R.id.result); initHandlerThraed(); } private void initHandlerThraed() { //創建HandlerThread實例 mHandlerThread = new HandlerThread("handler_thread"); //開始運行線程 mHandlerThread.start(); //獲取HandlerThread線程中的Looper實例 Looper loop = mHandlerThread.getLooper(); //創建Handler與該線程綁定。 mSubThreadHandler = new Handler(loop){ public void handleMessage(Message msg) { Log.i(TAG, "mSubThreadHandler handleMessage thread:"+Thread.currentThread()); switch(msg.what){ case MSG_GET: try { //模擬延時處理 Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } double number = Math.random(); String result = "number:"+number; //向ui線程發送消息,更新ui。 Message message = new Message(); message.what = MSG_RESULT; message.obj = result; mUiHandler.sendMessage(message); break; default: break; } }; }; } @Override protected void onDestroy() { super.onDestroy(); Log.i(TAG, "onDestroy"); //退出HandlerThread的Looper循環。 mHandlerThread.quit(); } }
上述代碼比較簡單,功能也比較簡單,可以在此基礎上進行擴展。
在Actvitiy創建的時候調用initHandlerThraed()函數:
-
創建HandlerThread線程
-
運行線程
-
獲取HandlerThread線程中的Looper實例
-
通過Looper實例創建Handler實例,從而使mSubThreadHandler與該線程連接到一起。
多次點擊按鈕,打印信息如下所示:
07-13 05:15:07.662: I/MainActivity(1472): onCreate thread:Thread[main,5,main] 07-13 05:15:45.382: I/MainActivity(1472): mSubThreadHandler handleMessage thread:Thread[handler_thread,5,main] 07-13 05:15:46.402: I/MainActivity(1472): mUiHandler handleMessage thread:Thread[main,5,main] 07-13 05:15:46.412: I/MainActivity(1472): mSubThreadHandler handleMessage thread:Thread[handler_thread,5,main] 07-13 05:15:47.412: I/MainActivity(1472): mUiHandler handleMessage thread:Thread[main,5,main] .....
點擊按鈕,向mSubThreadHandler發送消息,mSubThreadHandler中接收到消息進行處理,由打印可知mSubThreadHandler的handleMessage方法運行在子線程中。
模擬耗時操作,生成隨機數,然后向主線程中(mUiHandler)發送消息(Message)。
mUiHandler的handleMessage方法運行在主線程,可以用來更新Ui界面。
Activity銷毀的時候,調用mHandlerThread.quit(),退出HandlerThread的Looper循環。
效果圖如下:
【運行方式:右鍵項目:Run as -》Android Application (備注:Eclipse需要配置Android開發環境)】
三、源碼解析
源碼路徑路徑:frameworks/base/core/Java/android/os/HandlerThread.java
先看下HandlerThread的構造方法。
public class HandlerThread extends Thread { int mPriority; int mTid = -1; Looper mLooper; //@param name 線程名 public HandlerThread(String name) { super(name); mPriority = Process.THREAD_PRIORITY_DEFAULT; } /** * Constructs a HandlerThread. * @param name * @param priority The priority to run the thread at. The value supplied must be from * {@link android.os.Process} and not from java.lang.Thread. */ public HandlerThread(String name, int priority) { super(name); mPriority = priority; } 。。。。
HandlerThread是Thread(線程)的子類。創建一個HandlerThread實例,也就是創建了一個特殊的線程實例。
HandlerThread提供了兩個構造方法:
-
HandlerThread(String name) 參數為線程名稱,線程優先級為Process.THREAD_PRIORITY_DEFAULT。
-
HandlerThread(String name, int priority),name為線程名稱,priority為設置的線程優先級。
我們知道線程需要通過start()方法來運行線程,HandlerThread也是這樣的。接着看下線程運行的run()方法。
/** * Call back method that can be explicitly overridden if needed to execute some * setup before Looper loops. */ protected void onLooperPrepared() { } @Overridepublic void run() { //獲取進程id mTid = Process.myTid(); //創建Looper實例 Looper.prepare(); synchronized (this) { //獲取當前線程的Looper實例 mLooper = Looper.myLooper(); notifyAll(); } //設置線程優先級 Process.setThreadPriority(mPriority); onLooperPrepared(); //開始循環 Looper.loop(); mTid = -1; }
由run方法可知HandlerThrea線程運行創建了Looper實例,並開啟了Looper循環,循環從消息隊列中獲取消息並給Handler進行處理。對於Looper不太明白的可以參考這篇深入理解Handler、Looper、Messagequeue
onLooperPrepared()在Looper循環之前調用,如果需要在Looper循環之前執行一些設置,可以顯式覆蓋此方法。
接着看獲取Looper實例
//獲取HandlerThread線程中的Looper實例 Looper loop = mHandlerThread.getLooper();
對應源碼:
//此方法返回與此線程關聯的Looper。 如果此線程未啟動或由於任何原因isAlive()返回false,此方法將返回null。 public Looper getLooper() { if (!isAlive()) { return null; } // 如果這個線程已經啟動,將會被阻塞,直到mLooper被初始化為止。 synchronized (this) { while (isAlive() && mLooper == null) { try { wait(); } catch (InterruptedException e) { } } } return mLooper; }
mHandlerThread.getLooper()獲取與該線程綁定的Looper實例。mLooper是在HandlerThread的run()方法中賦值的(也就是在子線程中),getLooper是我們在主線程中調用,該方法會阻塞直到mLooper賦值。
然后demo中通過該looper實例創建Handler.
//創建Handler與該線程綁定。 mSubThreadHandler = new Handler(loop)
你可能會好奇為什么要這樣長久Handler而不是“new Handler()“這樣呢?因為我們要創建的Handler要與子線程綁定到一起,要處理子線程中的消息,所以要通過子線程中的looper(有線程對應的消息隊列)實例創建Handler。這樣通過mSubThreadHandler發送的消息會添加到子線程中的消息隊列中,然后Looper實例消息進行分發,交給mSubThreadHandler進行處理。
HandlerThread提供的線程退出方法:
public boolean quit() { Looper looper = getLooper(); if (looper != null) { looper.quit(); return true; } return false; } public boolean quitSafely() { Looper looper = getLooper(); if (looper != null) { looper.quitSafely(); return true; } return false; }
quit和quitSafely都是退出HandlerThread的消息循環。其分別調用Looper的quit和quitSafely方法。
quit方法會將消息隊列中的所有消息移除(延遲消息和非延遲消息)。
quitSafely會將消息隊列所有的延遲消息移除,非延遲消息派發出去讓Handler去處理。quitSafely相比於quit方法安全之處在於清空消息之前會派發所有的非延遲消息
HandlerThread適合處理本地IO讀寫操作(數據庫,文件),因為本地IO操作大多數的耗時屬於毫秒級別,對於單線程 + 異步隊列的形式 不會產生較大的阻塞。而網絡操作相對比較耗時,容易阻塞后面的請求,因此在這個HandlerThread中不適合加入網絡操作。
至此HandlerThread就說完了。有什么問題歡迎大家指正、交流。
四、其他補充
參考文章:
深入理解Handler、Looper、Messagequeue