深刻理解Android消息處理系統——Looper、Handler、Thread

熟悉Windows編程的朋友可能知道Windows程序是消息驅動的,而且有全局的消息循環系統。而Android應用程序也是消息驅動的,按道理來講也應該提供消息循環機制。實際上谷歌參考了Windows的消息循環機制,也在Android系統中實現了消息循環機制。Android經過Looper、Handler來實現消息循環機制,Android消息循環是針對線程的(每一個線程均可以有本身的消息隊列和消息循環)。本文深刻介紹一下Android消息處理系統原理。

Android系統中Looper負責管理線程的消息隊列和消息循環,具體實現請參考Looper的源碼。 能夠經過Loop.myLooper()獲得當前線程的Looper對象,經過Loop.getMainLooper()能夠得到當前進程的主線程的Looper對象。java

前面提到Android系統的消息隊列和消息循環都是針對具體線程的,一個線程能夠存在(固然也能夠不存在)一個消息隊列和一個消息循環(Looper),特定線程的消息只能分發給本線程,不能進行跨線程,跨進程通信。可是建立的工做線程默認是沒有消息循環和消息隊列的,若是想讓該線程具備消息隊列和消息循環,須要在線程中首先調用Looper.prepare()來建立消息隊列,而後調用Looper.loop()進入消息循環。以下例所示:android

class LooperThread extends Thread {
      public Handler mHandler;

      public void run() {
          Looper.prepare();

          mHandler = new Handler() {
              public void handleMessage(Message msg) {
                  // process incoming messages here               }
          };

          Looper.loop();
      }
  }

這樣你的線程就具備了消息處理機制了,在Handler中進行消息處理。編程

Activity是一個UI線程,運行於主線程中,Android系統在啓動的時候會爲Activity建立一個消息隊列和消息循環(Looper)。詳細實現請參考ActivityThread.java文件。安全

Handler的做用是把消息加入特定的(Looper)消息隊列中,並分發和處理該消息隊列中的消息。構造Handler的時候能夠指定一個Looper對象,若是不指定則利用當前線程的Looper建立。詳細實現請參考Looper的源碼。app

Activity、Looper、Handler的關係以下圖所示:框架

一個Activity中能夠建立多個工做線程或者其餘的組件,若是這些線程或者組件把他們的消息放入Activity的主線程消息隊列,那麼該消息就會在主線程中處理了。由於主線程通常負責界面的更新操做,而且Android系統中的weget不是線程安全的,因此這種方式能夠很好的實現Android界面更新。在Android系統中這種方式有着普遍的運用。ide

那麼另一個線程怎樣把消息放入主線程的消息隊列呢?答案是經過Handle對象,只要Handler對象以主線程的Looper建立,那麼調用Handler的sendMessage等接口,將會把消息放入隊列都將是放入主線程的消息隊列。而且將會在Handler主線程中調用該handler的handleMessage接口來處理消息。函數

這裏面涉及到線程同步問題,請先參考以下例子來理解Handler對象的線程模型:oop

一、首先建立MyHandler工程。學習

二、在MyHandler.java中加入以下的代碼:

package com.simon;import android.app.Activity;import android.os.Bundle;import android.os.Message;import android.util.Log;import android.os.Handler;public class MyHandler extends Activity {
	static final String TAG = "Handler";
	Handler h = new Handler(){
    	public void handleMessage (Message msg)
    	{
    		switch(msg.what)
    		{
    		case HANDLER_TEST:
    			Log.d(TAG, "The handler thread id = " + Thread.currentThread().getId() + "\n");
    			break;
    		}
    	}
    };

	static final int HANDLER_TEST = 1;
    /** Called when the activity is first created. */     @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d(TAG, "The main thread id = " + Thread.currentThread().getId() + "\n");

        new myThread().start();
        setContentView(R.layout.main);
    }

    class myThread extends Thread
    {
    	public void run()
    	{
    		Message msg = new Message();
    		msg.what = HANDLER_TEST;
    		h.sendMessage(msg);
    		Log.d(TAG, "The worker thread id = " + Thread.currentThread().getId() + "\n");
    	}
    }
}

在這個例子中咱們主要是打印,這種處理機制各個模塊的所處的線程狀況。以下是個人機器運行結果:

09-10 23:40:51.478: DEBUG/Handler(302): The main thread id = 1
09-10 23:40:51.569: DEBUG/Handler(302): The worker thread id = 8
09-10 23:40:52.128: DEBUG/Handler(302): The handler thread id = 1

咱們能夠看出消息處理是在主線程中處理的,在消息處理函數中能夠安全的調用主線程中的任何資源,包括刷新界面。工做線程和主線程運行在不一樣的線程中,因此必需要注意這兩個線程間的競爭關係。

上例中,你可能注意到在工做線程中訪問了主線程handler對象,並在調用handler的對象向消息隊列加入了一個消息。這個過程當中會不會出現消息隊列數據不一致問題呢?答案是handler對象不會出問題,由於handler對象管理的Looper對象是線程安全的,不論是加入消息到消息隊列和從隊列讀出消息都是有同步對象保護的,具體請參考Looper.java文件。上例中沒有修改handler對象,因此handler對象不可能會出現數據不一致的問題。

經過上面的分析,咱們能夠得出以下結論:

一、若是經過工做線程刷新界面,推薦使用handler對象來實現。

二、注意工做線程和主線程之間的競爭關係。推薦handler對象在主線程中構造完成(而且啓動工做線程以後不要再修改之,不然會出現數據不一致),而後在工做線程中能夠放心的調用發送消息SendMessage等接口。

三、除了2所述的hanlder對象以外的任何主線程的成員變量若是在工做線程中調用,仔細考慮線程同步問題。若是有必要須要加入同步對象保護該變量。

四、handler對象的handleMessage接口將會在主線程中調用。在這個函數能夠放心的調用主線程中任何變量和函數,進而完成更新UI的任務。

五、Android不少API也利用Handler這種線程特性,做爲一種回調函數的變種,來通知調用者。這樣Android框架就能夠在其線程中將消息發送到調用者的線程消息隊列之中,不用擔憂線程同步的問題。

深刻理解Android消息處理機制對於應用程序開發很是重要,也可讓你對線程同步有更加深入的認識。以上是最近Simon學習Android消息處理機制的一點兒總結,若有錯誤之處請不吝指教。

相關文章
相關標籤/搜索