Android多線程分析之三:Handler,Looper的實現,androidlooper
Android多線程分析之三:Handler,Looper的實現
羅朝輝 (http://www.cnblogs.com/kesalin/)CC 許可,轉載請註明出處
在前文《Android多線程分析之二:Thread的實現》中已經詳細分析了Android Thread 是如何建立,運行以及銷毀的,其重點是對相應 native 方法進行分析,今天我將聚焦於 Android Framework 層多線程相關的類:Handler, Looper, MessageQueue, Message 以及它們與Thread 之間的關係。可以用一個不太妥當的比喻來形容它們之間的關聯:如果把 Thread 比作生產車間,那麼 Looper 就是放在這車間裡的生產線,這條生產線源源不斷地從 MessageQueue 中擷取材料 Messsage,並分發處理 Message (由於Message 通常是完備的,所以 Looper 大多數情況下只是調度讓 Message 的 Handler 去處理 Message)。正是因為訊息需要在 Looper 中處理,而 Looper 又需運行在 Thread 中,所以不能隨隨便便在非 UI 線程中進行 UI 操作。 UI 操作通常會通過投遞訊息來實現,只有往正確的 Looper 投遞訊息才能得到處理,對於 UI 來說,這個 Looper 一定是運行在 UI 線程中。
在編寫 app 的過程中,我們常常會這樣來使用 Handler:
Handler mHandler = new Handler();mHandler.post(new Runnable(){@Overridepublic void run() {// do somework}});
或者如這系列文章第一篇中的樣本那樣:
private Handler mHandler= new Handler(){ @Override public void handleMessage(Message msg) { Log.i("UI thread", " >> handleMessage()"); switch(msg.what){ case MSG_LOAD_SUCCESS: Bitmap bitmap = (Bitmap) msg.obj; mImageView.setImageBitmap(bitmap); mProgressBar.setProgress(100); mProgressBar.setMessage("Image downloading success!"); mProgressBar.dismiss(); break; case MSG_LOAD_FAILURE: mProgressBar.setMessage("Image downloading failure!"); mProgressBar.dismiss(); break; } } }; Message msg = mHandler.obtainMessage(MSG_LOAD_FAILURE, null); mHandler.sendMessage(msg);
那麼,在 Handler 的 post/sendMessage 背後到底發生了什麼事情呢?下面就來解開這個謎團。
首先我們從 Handler 的建構函式開始分析:
final MessageQueue mQueue; final Looper mLooper; final Callback mCallback; final boolean mAsynchronous; public Handler(Looper looper, Callback callback, boolean async) { mLooper = looper; mQueue = looper.mQueue; mCallback = callback; mAsynchronous = async; } public Handler(Callback callback, boolean async) { 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 = callback; mAsynchronous = async; } public Handler() { this(null, false); }
上面列出了 Handler 的一些成員變數:
mLooper:線程的訊息處理迴圈,注意:並非每一個線程都有訊息處理迴圈,因此 Framework 中線程可以分為兩種:有 Looper 的和無 Looper 的。為了方便 app 開發,Framework 提供了一個有 Looper 的 Thread 實現:HandlerThread。在前一篇《Thread的實現》中也提到了兩種不同 Thread 的 run() 方法的區別。
/** * 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 { Looper mLooper; /** * Call back method that can be explicitly overridden if needed to execute some * setup before Looper loops. */ protected void onLooperPrepared() { } public void run() { mTid = Process.myTid(); Looper.prepare(); synchronized (this) { mLooper = Looper.myLooper(); notifyAll(); } 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(); } catch (InterruptedException e) { } } } return mLooper; }}
這個 HandlerThread 與 Thread 相比,多了一個類型為 Looper 成員變數 mLooper,它是在 run() 函數中由 Looper::prepare() 建立的,並儲存在 TLS 中:
/** 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::myLooper() 擷取建立的 Looper:
/** * Return the Looper object associated with the current thread. Returns * null if the calling thread is not associated with a Looper. */ public static Looper myLooper() { return sThreadLocal.get(); }
最後通過 Looper::Loop() 方法運行訊息處理迴圈:從 MessageQueue 中取出訊息,並分發處理該訊息,不斷地迴圈這個過程。這個方法將在後面介紹。
Handler 的成員變數 mQueue 是其成員變數 mLooper 的成員變數,這裡只是為了簡化書寫,單獨拿出來作為 Handler 的成員變數;成員變數 mCallback 提供了另一種使用Handler 的簡便途徑:只需實現回調介面 Callback,而無需子類化Handler,下面會講到的:
/** * Callback interface you can use when instantiating a Handler to avoid * having to implement your own subclass of Handler. */ public interface Callback { public boolean handleMessage(Message msg); }
成員變數 mAsynchronous 是標識是否非同步處理訊息,如果是的話,通過該 Handler obtain 得到的訊息都被強制設定為非同步。
同是否有無 Looper 來區分 Thread 一樣,Handler 的建構函式也分為內建 Looper 和外部 Looper 兩大類:如果提供了 Looper,在訊息會在該 Looper 中處理,否則訊息就會在當前線程的 Looper 中處理,注意這裡要確保當前線程一定有 Looper。所有的 UI thread 都是有 Looper 的,因為 view/widget 的實現中大量使用了訊息,需要 UI thread 提供 Looper 來處理,可以參考view.java:
view.java public boolean post(Runnable action) { final AttachInfo attachInfo = mAttachInfo; if (attachInfo != null) { return attachInfo.mHandler.post(action); } // Assume that post will succeed later ViewRootImpl.getRunQueue().post(action); return true; }
ViewRootImpl.java private void performTraversals() { .... // Execute enqueued actions on every traversal in case a detached view enqueued an action getRunQueue().executeActions(attachInfo.mHandler); ... } static RunQueue getRunQueue() { RunQueue rq = sRunQueues.get(); if (rq != null) { return rq; } rq = new RunQueue(); sRunQueues.set(rq); return rq; } /** * The run queue is used to enqueue pending work from Views when no Handler is * attached. The work is executed during the next call to performTraversals on * the thread. * @hide */ static final class RunQueue { ... void executeActions(Handler handler) { synchronized (mActions) { final ArrayList<HandlerAction> actions = mActions; final int count = actions.size(); for (int i = 0; i < count; i++) { final HandlerAction handlerAction = actions.get(i); handler.postDelayed(handlerAction.action, handlerAction.delay); } actions.clear(); } } }
從上面的代碼可以看出,作為所有控制項基類的 view 提供了 post 方法,用於向 UI Thread 發送訊息,並在 UI Thread 的 Looper 中處理這些訊息,而 UI Thread 一定有 Looper 這是由 ActivityThread 來保證的:
public final class ActivityThread {... final Looper mLooper = Looper.myLooper();}
UI 操作需要向 UI 線程發送訊息並在其 Looper 中處理這些訊息。這就是為什麼我們不能在非 UI 線程中更新 UI 的原因,在控制項在非 UI 線程中構造 Handler 時,要麼由於非 UI 線程沒有 Looper 使得擷取 myLooper 失敗而拋出 RunTimeException,要麼即便提供了 Looper,但這個 Looper 並非 UI 線程的 Looper 而不能處理控制項訊息。為此在 ViewRootImpl 中有一個強制檢測 UI 操作是否是在 UI 線程中處理的方法 checkThread():該方法中的 mThread 是在 ViewRootImpl 的建構函式中賦值的,它就是 UI 線程;該方法中的 Thread.currentThread() 是當前進行 UI 操作的線程,如果這個線程不是非 UI 線程就會拋出異常CalledFromWrongThreadException。
void checkThread() { if (mThread != Thread.currentThread()) { throw new CalledFromWrongThreadException( "Only the original thread that created a view hierarchy can touch its views."); } }
如果修改《使用Thread非同步下載映像》中樣本,下載完映像 bitmap 之後,在 Thread 的 run() 函數中設定 ImageView 的映像為該 bitmap,即會拋出上面提到的異常:
W/dalvikvm(796): threadid=11: thread exiting with uncaught exception (group=0x40a71930)E/AndroidRuntime(796): FATAL EXCEPTION: Thread-75E/AndroidRuntime(796): android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.E/AndroidRuntime(796): at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:4746)E/AndroidRuntime(796): at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:823)E/AndroidRuntime(796): at android.view.View.requestLayout(View.java:15473)E/AndroidRuntime(796): at android.view.View.requestLayout(View.java:15473)E/AndroidRuntime(796): at android.view.View.requestLayout(View.java:15473)E/AndroidRuntime(796): at android.view.View.requestLayout(View.java:15473)E/AndroidRuntime(796): at android.view.View.requestLayout(View.java:15473)E/AndroidRuntime(796): at android.widget.ImageView.setImageDrawable(ImageView.java:406)E/AndroidRuntime(796): at android.widget.ImageView.setImageBitmap(ImageView.java:421)E/AndroidRuntime(796): at com.example.thread01.MainActivity$2$1.run(MainActivity.java:80)
Handler 的建構函式暫且介紹到這裡,接下來介紹:handleMessage 和 dispatchMessage:
/** * Subclasses must implement this to receive messages. */ public void handleMessage(Message msg) { } /** * Handle system messages here. */ public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }
前面提到有兩種方式來設定處理訊息的代碼:一種是設定 Callback 回調,一種是子類化 Handler。而子類化 Handler 其子類就要實現 handleMessage 來處理自訂的訊息,如前面的匿名子類樣本一樣。dispatchMessage 是在 Looper::Loop() 中被調用,即它是線上程的訊息處理迴圈中被調用,這樣就能讓 Handler 不斷地處理各種訊息。在 dispatchMessage 的實現中可以看到,如果 Message 有自己的訊息處理回調,那麼就優先調用訊息自己的訊息處理回調:
private static void handleCallback(Message message) { message.callback.run(); }
否則看Handler 是否有訊息處理回調 mCallback,如果有且 mCallback 成功處理了這個訊息就返回了,否則就調用 handleMessage(通常是子類的實現) 來處理訊息。
在分析 Looper::Loop() 這個關鍵函數之前,先來理一理 Thread,Looper,Handler,MessageQueue 的關係:Thread 需要有 Looper 才能處理訊息(也就是說 Looper 是運行在 Thread 中),這是通過在自訂 Thread 的 run() 函數中調用 Looper::prepare() 和 Looper::loop() 來實現,然後在 Looper::loop() 中不斷地從 MessageQueue 擷取由 Handler 投遞到其中的 Message,並調用 Message 的成員變數 Handler 的 dispatchMessage 來處理訊息。
下面先來看看 Looper 的建構函式:
final MessageQueue mQueue; final Thread mThread; volatile boolean mRun; private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mRun = true; mThread = Thread.currentThread(); }
Looper 的建構函式很簡單,建立MessageQueue,儲存當前線程到 mThread 中。但它是私人的,只能通過兩個靜態函數 prepare()/prepareMainLooper() 來調用,前面已經介紹了 prepare(),下面來介紹 prepareMainLooper():
/** * Initialize the current thread as a looper, marking it as an * application's main looper. The main looper for your application * is created by the Android environment, so you should never need * to call this function yourself. See also: {@link #prepare()} */ public static void prepareMainLooper() { prepare(false); synchronized (Looper.class) { if (sMainLooper != null) { throw new IllegalStateException("The main Looper has already been prepared."); } sMainLooper = myLooper(); } }
prepareMainLooper 是通過調用 prepare 實現的,不過傳入的參數為 false,表示 main Looper 不允許中途被中止,建立之後將looper 保持在靜態變數 sMainLooper 中。整個 Framework 架構只有兩個地方調用了 prepareMainLooper 方法:
SystemServer.java 中的 ServerThread,ServerThread 的重要性就不用說了,絕大部分 Android Service 都是這個線程中初始化的。這個線程是在 Android 啟動過程中的 init2() 方法啟動的:
public static final void init2() { Slog.i(TAG, "Entered the Android system server!"); Thread thr = new ServerThread(); thr.setName("android.server.ServerThread"); thr.start(); }class ServerThread extends Thread { @Override public void run() { ... Looper.prepareMainLooper(); ... Looper.loop(); Slog.d(TAG, "System ServerThread is exiting!"); }}
以及 ActivityThread.java 的 main() 方法:
public static void main(String[] args) { .... Looper.prepareMainLooper(); ActivityThread thread = new ActivityThread(); thread.attach(false); if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler(); } AsyncTask.init(); Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited"); }
ActivityThread 的重要性也不言而喻,它是 Activity 的主線程,也就是 UI 線程。注意這裡的 AsyncTask.init() ,在後面介紹 AsyncTask 時會詳細介紹的,這裡只提一下:AsyncTask 能夠進行 UI 操作正是由於在這裡調用了 init()。
有了前面的鋪墊,這下我們就可以來分析 Looper::Loop() 這個關鍵函數了:
/** * Run the message queue in this thread. Be sure to call * {@link #quit()} to end the loop. */ public static void loop() { 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; ... for (;;) { Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } msg.target.dispatchMessage(msg); msg.recycle(); } }
loop() 的實現非常簡單,一如前面一再說過的那樣:不斷地從 MessageQueue 中擷取訊息,分發訊息,回收訊息。從上面的代碼可以看出,loop() 僅僅是一個不斷迴圈作業的生產流水線,而 MessageQueue 則為它提供原材料 Message,讓它去分發處理。至於 Handler 是怎麼提交訊息到 MessageQueue 中,MessageQueue 又是怎麼管理訊息的,且待下文分解。
救命 android線程間通訊 handler message之類的
線程間通訊(三種方式)::
public class AnrActivity extends Activity implements OnClickListener {
TextView text;
Handler handler;
ProgressBar bar;
private class MyAsycnTask extends AsyncTask<URL, Integer, String>{
@Override
protected void onProgressUpdate(Integer... values) {
bar.setProgress(values[0]);
text.setText("進度:"+values[0]+"%");
super.onProgressUpdate(values);
}
@Override
protected String doInBackground(URL... params) {
for (int i = 0; i <100; i++) {
try {
publishProgress(i+1);
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return "success";
}
@Override
protected void onPostExecute(String result) {
text.setText(result);
}
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button down = (Button) this.findViewById(R.id.button1);
Button receiver = (Button) this.findViewById(R.id.button2);
Button update = (Button)this.findViewById(R.id.button3);
bar = (ProgressBar)this.findViewById(R.id.progressBar1);
text = (TextView)this.findViewById(R.id.text);
update.setOnClickListener(this);
down.setOnClickListener(this);
receiver.setOnClickListener(this);
}
@Override
public void onClick(final View v) {
switch (v.getId()) {
case R.id.button1:
//通過 Asycntask 進行通訊
MyAsycnTask myAsycnTask = new MyAsycnTask();
myAsycnT......餘下全文>>
ANDROID中handler的問題
Handler不等於使用線程。
傳統的UI架構,如swing。將繪製展示、事件派發都放在主線程(UI)線程中進行。
UI線程的實現模型通常是一個死迴圈,不斷接受Message。組織派發
android中Handler-MessageQueue-Looper,三者構成了這種死迴圈+訊息通訊的模型。此處的postDelayed實際上是將一個Runnable任務投入了MessageQueue中,並期望在3000毫秒後執行。
另外不要誤以為Runnable是線程。在java.util.concurrent中,Dogn大神已經明確將執行過程與任務分割出來。Runnable介面只是表示一項任務,既可以同步執行,也可以在新線程中執行。
為何需要Handler而不用Thread。除了訊息模型是UI架構的傳統模式外,還涉及到UI組件不允許跨線程訪問的限制。無論是.NET也好,swing也好,android也好,不允許在非UI線程中操作這一點都一樣。
Handler便是android架構中非同步線程代碼到達同步線程的官方通道。從另一個角度說,這種基於訊息模型的通訊模式有時也很有用。相關的例子有IntentService,HandlerThread等。