本人只是 Android小菜一個,寫技術文檔只是爲了總結本身在最近學習到的知識,歷來不敢爲人師,若是裏面有些不正確的地方請你們盡情指出,謝謝!java
HandlerThread
是Android
提供用來建立含有Looper
線程的,其實在以前分析IntentService
的博文中已經看到了它的應用,再來回顧下IntentService
的啓動過程:android
public void onCreate() {
// TODO: It would be nice to have an option to hold a partial wakelock
// during processing, and to have a static startService(Context, Intent)
// method that would launch the service & hand off a wakelock.
super.onCreate();
// 建立包含 Looper 的線程並啓動之
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
// 經過新線程的 Looper 建立 Handler 實例
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
複製代碼
這段IntentService
的啓動代碼中直接使用到了HandlerThread
,但當時只是一筆帶過並無仔細分析HandlerThread
的使用方法和實現原理,本文將詳細講解如何在項目中使用HandlerThread
和其內部的實現原理。shell
本文假設您對
Handler,Thread,Looper,Message 和 MessageQueue
相關知識有了必定的瞭解,因此涉及到它們的地方,只會稍做說明再也不深刻分析。安全
在講解其具體使用方法前,仍是先來看下對HandlerThread
的聲明:ide
/** * 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. */
public class HandlerThread extends Thread { ... }
複製代碼
從這段聲明裏能夠看到:HandlerThread
可以很方便地啓動一個帶有looper
的線程,而這個looper
能夠用來建立handler
。這句話裏隱含了幾點重要知識:函數
HandlerThread
是一個Thread
線程,具備線程的特性。Android
中默認線程沒有looper
,若是想建立帶有looper
的線程須要在建立的過程當中主動創造looper
對象。Handler
中必需要有looper
,它是整個消息查詢、分發、處理的核心,在建立Handler
的過程當中能夠指定任意線程的looper
對象。如今經過一個簡單的示例演示下HandlerThread
的使用方法:oop
public class MainActivity extends Activity {
private static final String TAG = "Android_Test";
private Button mButton;
private TextView mText;
// 新線程和與之相關聯的 Handler 對象
private HandlerThread mHanderThread;
private Handler mThreadHandler;
// 和主線程相關的 Handler 對象
private Handler mUiHandler;
// 用於子線程和主線程中的消息分發
private static final int MESSAGE_CODE_GET = 1;
private static final int MESSAGE_CODE_SET = 2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mButton = (Button) findViewById(R.id.main_button);
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 主線程經過子線程 Handler 分發消息,以達到在子線程中處理耗時任務的目的。
mThreadHandler.sendEmptyMessage(MESSAGE_CODE_GET);
}
});
mText = (TextView) findViewById(R.id.main_text);
// 建立 HandlerThread 並啓動新線程
mHanderThread = new HandlerThread("HandlerThread");
mHanderThread.start();
// 經過新線程中的 looper 建立相關的 Handler 對象
mThreadHandler = new Handler(mHanderThread.getLooper()) {
@Override
public void handleMessage(Message msg) {
Log.i(TAG, "mThreadHandler's thread: " + Thread.currentThread().getName());
if (msg.what == MESSAGE_CODE_GET) {
try {
// 休眠 5 秒,模擬子線程處理耗時任務的過程。
Thread.sleep(5 * 1000);
} catch (InterruptedException ie) {
ie.printStackTrace();
}
// 向主線程 Handler 發送處理結果
mUiHandler.sendEmptyMessage(MESSAGE_CODE_SET);
}
}
};
mUiHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
Log.i(TAG, "mUiHandler's thread: " + Thread.currentThread().getName());
if (msg.what == MESSAGE_CODE_SET) {
// 主線程接收來自子線程的消息就行後續處理,這裏是顯示當前時間信息。
mText.setText(String.valueOf(SystemClock.uptimeMillis()));
}
}
};
}
}
複製代碼
這個示例的主要功能是主線程中發起任務,在子線程中處理這些耗時任務,處理完成後通知主線程並更新界面,並打印出運行過程,從下面的運行結果能夠看到:耗時任務確實是在子線程中執行的。post
03-01 10:04:57.311 30673 30723 I Android_Test: mThreadHandler's thread: HandlerThread
03-01 10:05:02.313 30673 30673 I Android_Test: mUiHandler's thread: main
複製代碼
從上面的示例能夠總結獲得HandlerThread
的使用方法:學習
HandlerThread
對象並運行它,在建立過程當中須要指定線程名字;HandlerThread
對象中的looper
並經過它來構造一個子線程Handler
對象;Handler
對象向子線程分發任務;HandlerThread
和普通的Thread
的區別就在於其內部是包含Looper
的,因此咱們分析的重點就是它是怎麼建立使用Looper
以及在使用後如何退出。首先來看下它的構造函數:ui
public class HandlerThread extends Thread {
// 線程優先級
int mPriority;
// 線程號
int mTid = -1;
// 線程內部的 Looper 對象
Looper mLooper;
private @Nullable Handler mHandler;
// 只指定線程名字並使用默認的線程優先級來構造 HandlerThread 對象
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. */
// 同時指定線程名字和優先級來構造 HandlerThread 對象
public HandlerThread(String name, int priority) {
super(name);
mPriority = priority;
}
// 省略其餘內容
...
}
複製代碼
因爲HandlerThread
是直接繼承Thread
的,因此在經過start()
啓動線程後,其中的run()
就會啓動,這也是線程內部的核心方法,來看下其實現:
@Override
public void run() {
mTid = Process.myTid();
// 建立一個和當前線程有關的 Looper 對象
Looper.prepare();
synchronized (this) {
// 獲得當前線程的 Looper 對象後喚醒等待
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
// 調用回調方法,能夠在開始消息輪詢以前進行某些初始化設置,默認是空方法。
onLooperPrepared();
// 啓動消息輪詢,進行消息的查詢分發和處理。
Looper.loop();
mTid = -1;
}
複製代碼
這段代碼就是HandlerThread
中建立Looper
對象並啓動消息循環的核心,咱們來一步步分析其重要邏輯。
在覈心代碼run()
中首先看到的是Looper.prepare()
,其做用就是建立當前線程的Looper
對象:
/** Initialize the current thread as a looper. * This gives you a chance to create handlers that then reference * this looper, before actually starting the loop. Be sure to call * {@link #loop()} after calling this method, and end it by calling * {@link #quit()}. */
public static void prepare() {
prepare(true);
}
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));
}
複製代碼
在使用Looper.prepare()
建立Looper
對象的過程當中利用ThreadLocal
把這個對象和當前線程創建了關聯。
ThreadLocal
是一個能夠存儲線程局部變量的類,若是你們感興趣能夠自行查閱相關資料,在這裏就不對其進行詳細講述了。
建立完Looper
對象後會在同步代碼塊裏去喚醒等待,那這個等待會發生在何時呢?記得示例中是經過getLooper()
獲得Looper
對象的,來看下它的內部實現:
/** * This method returns the Looper associated with this thread. If this thread not been started * or for any reason isAlive() returns false, this method will return null. If this thread * has been started, this method will block until the looper has been initialized. * @return The looper. */
public Looper getLooper() {
// 線程沒有啓動或者已經死亡時返回 null
if (!isAlive()) {
return null;
}
// If the thread has been started, wait until the looper has been created.
synchronized (this) {
// 線程已經啓動可是 Looper 對象尚未建立完成時等待
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
// 等待結束說明此時 Looper 對象已經建立完成,返回之。
return mLooper;
}
複製代碼
在這裏看到當「線程已經啓動可是Looper
對象尚未建立完成」時會進行等待,當建立完成時會喚醒等待,這時getLooper()
就能夠返回已經建立完成的Looper
對象了。之因此須要這個「等待-喚醒」機制,由於獲取Looper
是在主線程中進行的,而建立Looper
是在子線程中進行的,必須使用這個機制來完成二者的狀態同步。
前面已經講了Looper
對象的建立以及如何在主線程中獲取,那麼如何經過Looper.loop()
開啓循環呢?
/** * Run the message queue in this thread. Be sure to call * {@link #quit()} to end the loop. */
public static void loop() {
// 獲取Looper對象
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
// 獲取消息隊列
final MessageQueue queue = me.mQueue;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
// Allow overriding a threshold with a system prop. e.g.
// adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'
final int thresholdOverride =
SystemProperties.getInt("log.looper."
+ Process.myUid() + "."
+ Thread.currentThread().getName()
+ ".slow", 0);
boolean slowDeliveryDetected = false;
// 開啓一個無限循環來從消息隊列中獲取消息
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// This must be in a local variable, in case a UI event sets the logger
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
final long traceTag = me.mTraceTag;
long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;
if (thresholdOverride > 0) {
slowDispatchThresholdMs = thresholdOverride;
slowDeliveryThresholdMs = thresholdOverride;
}
final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0);
final boolean logSlowDispatch = (slowDispatchThresholdMs > 0);
final boolean needStartTime = logSlowDelivery || logSlowDispatch;
final boolean needEndTime = logSlowDispatch;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
final long dispatchEnd;
try {
// 獲取到消息後,分發到 target 去處理。
msg.target.dispatchMessage(msg);
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
if (logSlowDelivery) {
if (slowDeliveryDetected) {
if ((dispatchStart - msg.when) <= 10) {
Slog.w(TAG, "Drained");
slowDeliveryDetected = false;
}
} else {
if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
msg)) {
// Once we write a slow delivery log, suppress until the queue drains.
slowDeliveryDetected = true;
}
}
}
if (logSlowDispatch) {
showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg);
}
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
// 回收消息對象
msg.recycleUnchecked();
}
}
複製代碼
這段代碼很是長,在分析的時候不須要弄懂每一行的意思,只須要了解其中關於消息的大體處理流程便可,你們若是不想去看這大段代碼,只需關注添加註釋的幾行便可,其基本流程是:經過一個無限循環從消息隊列中查詢Message
消息,若是查詢不到就等待,若是查詢到就交給其target
來處理,最後要回收資源。
在使用HandlerThread
+Handler
在子線程處理耗時任務後而且再也不須要時,必需要退出Looper
的消息循環,能夠經過quit()
:
/** * Quits the handler thread's looper. * <p> * Causes the handler thread's looper to terminate without processing any * more messages in the message queue. * </p><p> * Any attempt to post messages to the queue after the looper is asked to quit will fail. * For example, the {@link Handler#sendMessage(Message)} method will return false. * </p><p class="note"> * Using this method may be unsafe because some messages may not be delivered * before the looper terminates. Consider using {@link #quitSafely} instead to ensure * that all pending work is completed in an orderly manner. * </p> */
public boolean quit() {
Looper looper = getLooper();
if (looper != null) {
looper.quit();
return true;
}
return false;
}
複製代碼
這份方法能夠退出Looper
循環同時會把當前消息隊列中的全部消息都拋棄,也沒法再向該消息隊列中發送消息。但有時咱們並不想直接清空消息隊列,這時可使用另一種方式:
/** * Quits the handler thread's looper safely. * <p> * Causes the handler thread's looper to terminate as soon as all remaining messages * in the message queue that are already due to be delivered have been handled. * Pending delayed messages with due times in the future will not be delivered. * </p><p> * Any attempt to post messages to the queue after the looper is asked to quit will fail. * For example, the {@link Handler#sendMessage(Message)} method will return false. * </p><p> * If the thread has not been started or has finished (that is if * {@link #getLooper} returns null), then false is returned. * Otherwise the looper is asked to quit and true is returned. * </p> * * @return True if the looper looper has been asked to quit or false if the * thread had not yet started running. */
public boolean quitSafely() {
Looper looper = getLooper();
if (looper != null) {
looper.quitSafely();
return true;
}
return false;
}
複製代碼
這個方法能夠更安全地退出,它會讓消息隊列中的非延遲消息繼續獲得處理,是更推薦的退出方式。
本文介紹了HandlerThread
的使用方法並分析其源碼,經過分析源碼,咱們瞭解到了其內部Looper
的建立、獲取、開啓、退出的過程,加深了對HandlerThread
原理的理解,更有利於之後的使用。