Handler,Looper,Message,MessageQueue,HandlerThread使用總結(上)

在安卓程序中,經常會有一些耗時的操作例如下載,網絡訪問等,如果將這些放在主線程執行,會很耗時,這樣可能會導致一個異常 叫ANR異常(Application Not Responding)將會阻塞UI線程,從而會導致程序無響應。因此我們會將一些耗時操作放在子線程進行,但是由於android的UI操作並不是線程安全的,因此如果多個線程同時操作UI的話,會導致線程安全問題,因此android制訂了一條規則,只允許UI線程(即主線程)進行UI操作,那麼我們如何知道子線程何時操作完成呢?例如子線程下載好圖片以後通知主線程進行視圖更新。

Message簡介

子線程任務完成以後需要發送消息給主線程,這個消息就是Message的一個實例,定義Message時需要定義三個實例變量

1、what   int型消息代碼,用來描述消息

2、obj      隨消息發送的用戶指定對象

3、target  處理該消息的Handler


創建Message的方法

1、new一個就行

2、使用Handler.obtainMessage(...)該方法可以使我們從公共循環池中獲取到一個Message實例,效率會提高



因此我們使用該方法會好些


MessageQueue簡介

MessageQueue(消息隊列)用來存放Message,採用先進先出的方式發出Message,


Looper簡介

Looper叫做消息循環,他會不斷的檢查 MessageQueue上是否有新消息,然後抓取消息,完成指定的任務。每個線程有且只有一個Looper對象,用來管理MessageQueue。主線程也有Looper,它會自動創建,主線程的所有工作都是由他的Looper完成的。Looper主要有兩個方法

1、Looper.prepare     用以啓用Looper

2、Looper.loop           讓Looper開始工作,從消息隊列中抓取處理消息



Handler簡介

要處理消息以及消息指定的任務時就需要用到Handler,她可以發出新消息到MessageQueue上,也可以讀取Looper從MessageQueue上獲取的消息。一個Handler僅與一個Looper相關聯,一個Message也僅與一個目標Hander相關聯。

Handler發送消息的方法:


Looper獲取到消息後,會交由消息的目標(即消息的target屬性)處理,消息一般是在Handler.handleMessage(...)方法中處理



關係圖示




在簡單瞭解過這些知識後,我們寫一個小程序,利用了Handler與Message。該應用的主界面僅有一個ImageView故代碼不再給出,主要功能是開啓一個子線程模擬下載圖片,然後下載完成後在UI線程上進行更新。


主要代碼

聲明handler,模擬下載到的圖片的資源Id數組

<span style="font-size:18px;"><pre name="code" class="java"><span style="font-size:18px;"><span style="white-space:pre">	</span>private ImageView mImageView;
	// 定義一個Handler
	private Handler mHandler;

	private int mIndex;
	// 存放照片資源id的數組
	int[] imageIds = new int[] { R.drawable.pic1, R.drawable.pic2,
			R.drawable.pic3, R.drawable.pic4

	};</span></span>




模擬下載圖片的方法

<span style="font-size:18px;">// 模擬下載的操作,隨機生成一個數字
	public int virtualDown() {
		Random ran = new Random();
		int value = ran.nextInt(5);
		return value;
	}</span>



使用TimerTask開啓一個下載的子線程,獲取到消息,併發送

<span style="font-size:18px;"><span style="font-size:18px;">// 開啓一個TimerTask模擬下載的子線程
		new Timer().schedule(new TimerTask() {

			@Override
			public void run() {

				Bundle bundle = new Bundle();
				bundle.putInt("value", virtualDown());
				// 獲取到消息
				// Message msg = new Message();
				Message msg = mHandler.obtainMessage();
				msg.what = 0x1233;
				msg.setData(bundle);
				// 發送消息,並將生成的數字傳遞過去
				mHandler.sendMessage(msg);
				

			}
		}, 0, 2000);
	}</span></span>


生成handle實例並在handleMessage中處理消息

<span style="font-size:18px;"><span style="white-space:pre">	</span>mHandler = new Handler() {

			@Override
			// 處理消息的方法
			public void handleMessage(Message msg) {
				// what屬性是消息的標識,用來區分每個消息
				if (msg.what == 0x1233) {
					mIndex = msg.getData().getInt("value");
					// 設置圖片的顯示,這是在主線程進行的
					switch (mIndex) {
					case 0:
						mImageView.setImageResource(imageIds[0]);
						break;
					case 1:
						mImageView.setImageResource(imageIds[1]);
						break;
					case 2:
						mImageView.setImageResource(imageIds[2]);
						break;
					case 3:
						mImageView.setImageResource(imageIds[3]);
						break;
					}
				}
			}
		};</span>



運行程序我們就可以看到主界面的圖片每隔兩秒就會變化一次了,當然圖片是事先準備好的,並非下載的,過段時間博主會實現真正的下載。


在上面的程序中爲什麼沒有見到Looper呢?這是因爲主線程在啓動過程中自動創建了Looper。而子線程默認是不帶Looper的,所以我們就需要自己創建Looper,這時候就需要用到上述的兩個方法,Looper.prepare和Looper.loop啓動Looper了,官方的API Demo的示例爲:

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();
		}
	}






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