Android消息隊列(三)--多線程與消息處理

三,Android消息隊列--多線程與消息處理

原文地址:http://www.e800.com.cn/articles/2011/0720/491429_2.shtml

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

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

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的源碼。

一個Activity中可以創建多個工作線程或者其他的組件,如果這些線程或者組件把他們的消息放入Activity的主線程消息隊列,那麼該消息就會在 主線程中處理了。因爲主線程一般負責界面的更新操作,並且Android系統中的weget不是線程安全的,所以這種方式可以很好的實現Android界 面更新。在Android系統中這種方式有着廣泛的運用。

那麼另外一個線程怎樣把消息放入主線程的消息隊列呢?

答案是通過Handle對象,只要Handler對象以主線程的Looper創建,那麼調用 Handler的sendMessage等接口,將會把消息放入隊列都將是放入主線程的消息隊列。並且將會在Handler主線程中調用該handler 的handleMessage接口來處理消息。

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

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對象不可能會出現數據不 一致的問題。

通過上面的分析,我們可以得出如下結論:

1、如果通過工作線程刷新界面,推薦使用handler對象來實現。

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

3、除了2所述的hanlder對象之外的任何主線程的成員變量如果在工作線程中調用,仔細考慮線程同步問題。如果有必要需要加入同步對象保護該變量。

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

5、Android很多API也利用Handler這種線程特性,作爲一種回調函數的變種,來通知調用者。這樣Android框架就可以在其線程中將消息發送到調用者的線程消息隊列之中,不用擔心線程同步的問題。

深入理解Android消息處理機制對於應用程序開發非常重要,也可以讓你對線程同步有更加深刻的認識。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章