Android應用中的訊息迴圈由Looper和Handler配合完成,Looper類用於封裝訊息迴圈,類中有個MessageQueue訊息佇列;Handler類封裝了訊息投遞和訊息處理等功能。
系統預設情況下只有主線程(即UI線程)綁定Looper對象,因此在主線程中可以直接建立Handler的執行個體,但是在子線程中就不能直接new出Handler的執行個體了,因為子線程預設並沒有Looper對象,此時會拋出RuntimeException異常:
瀏覽下Handler的預設建構函式就一目瞭然了:
public Handler() { if (FIND_POTENTIAL_LEAKS) { final Class<? extends Handler> klass = getClass(); if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) && (klass.getModifiers() & Modifier.STATIC) == 0) { Log.w(TAG, "The following Handler class should be static or leaks might occur: " + klass.getCanonicalName()); } } mLooper = Looper.myLooper(); if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); } mQueue = mLooper.mQueue; mCallback = null; }
如果需要在子線程中使用Handler類,首先需要建立Looper類執行個體,這時可以通過Looper.prepare()和Looper.loop()函數來實現的。閱讀Framework層源碼發現,Android為我們提供了一個HandlerThread類,該類繼承Thread類,並使用上面兩個函數建立Looper對象,而且使用wait/notifyAll解決了多線程中子線程1擷取子線程2的Looper對象為空白的問題。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. */public class HandlerThread extends Thread {private int mPriority; // 線程優先順序private int mTid = -1; // 線程IDprivate Looper mLooper; // 我們需要的Looper對象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;}/** * 在Looper.loop()之前執行動作的回呼函數 */protected void onLooperPrepared() {}public void run() {mTid = Process.myTid();Looper.prepare(); // 建立本線程的Looper對象synchronized (this) {mLooper = Looper.myLooper();notifyAll(); //通知所有等待該線程Looper對象的其他子線程,本線程的Looper對象已就緒}Process.setThreadPriority(mPriority);onLooperPrepared(); //回呼函數Looper.loop(); //開始訊息佇列迴圈mTid = -1;}/** * This method returns the Looper associated with this thread. If this * thread not been started or for any reason is 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() {if (!isAlive()) {return null;}// If the thread has been started, wait until the looper has been// created.synchronized (this) {while (isAlive() && mLooper == null) {try {wait(); //Looper對象未建立好,等待} catch (InterruptedException e) {}}}return mLooper;}/** * Ask the currently running looper to quit. 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. */public boolean quit() {Looper looper = getLooper();if (looper != null) {looper.quit();return true;}return false;}/** * Returns the identifier of this thread. See Process.myTid(). */public int getThreadId() {return mTid;}}