Handler,Looper,Message,MessageQueue,HandlerThread使用總結(上)
在安卓程式中,經常會有一些耗時的操作例如下載,網路訪問等,如果將這些放在主執行緒執行,會很耗時,這樣可能會導致一個異常 叫ANR異常(Application Not Responding)將會阻塞UI執行緒,從而會導致程式無響應。因此我們會將一些耗時操作放在子執行緒進行,但是由於android的UI操作並不是執行緒安全的,因此如果多個執行緒同時操作UI的話,會導致執行緒安全問題,因此android制訂了一條規則,只允許UI執行緒(即主執行緒)進行UI操作,那麼我們如何知道子執行緒何時操作完成呢?例如子執行緒下載好圖片以後通知主執行緒進行檢視更新。
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();
}
}