在Android中备禀,Handler常常用來在子線程中更新View。示例代碼如下:
public class MainActivity extends ActionBarActivity {
private Handler handler1=new Handler(){
@Override
public void handleMessage(Message msg){
//to do sth
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Thread(new Runnable(){
@Override
public void run() {
Message msg=Message.obtain();
msg.what=1;
Bundle bundle = new Bundle();
bundle.putString("info", "info");
msg.setData(bundle);
handler1.sendMessage(msg);
}
}).start();
}
}
但是奈揍,上述代碼僅僅是Handler的一個(gè)常見的使用場景而已曲尸。之前做過一個(gè)項(xiàng)目,項(xiàng)目中有一個(gè)相冊圖片加載模塊男翰,在這個(gè)模塊中另患,我建立了一個(gè)后臺(tái)輪詢線程mPollingThread并在mPollingThread的run方法中初始化了mPollingThreadHandler,每當(dāng)我們在LruCache中沒有找到這次要加載的圖片時(shí),就新建一個(gè)任務(wù)(其實(shí)就是一個(gè)Runnable對象)蛾绎,并將任務(wù)添加到我們的任務(wù)隊(duì)列中昆箕,之后會(huì)通過mPollingThreadHandler發(fā)送一個(gè)消息給后臺(tái)輪詢線程鸦列,后臺(tái)輪詢線程收到消息后會(huì)從任務(wù)隊(duì)列中取出一個(gè)任務(wù)執(zhí)行,核心代碼如下:
package com.example.myimageloader;
import java.util.concurrent.Semaphore;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.widget.ImageView;
public class ImageLoader {
private Thread mPoolingThread; //后臺(tái)輪詢線程
private Handler mPoolingThreadHandler; //后臺(tái)輪詢線程對應(yīng)的Handler
private Semaphore mSemaphorePoolingThreadHandler=new Semaphore(0);
private ImageLoader(){
mPoolingThread=new Thread(){
@Override
public void run(){
Looper.prepare();
mPoolingThreadHandler=new Handler(){
@Override
public void handleMessage(Message msg){
//從任務(wù)隊(duì)列中取出一個(gè)任務(wù)鹏倘,并交給線程池執(zhí)行
//......
}
};
//mPoolingThreadHandler初始化完成薯嗤,釋放一個(gè)信號(hào)量
mSemaphorePoolingThreadHandler.release();
Looper.loop();
}
};
mPoolingThread.start();
}
public void loadImage(final String path,final ImageView imageView){
//沒有在LruCache中找到相應(yīng)的圖片
//創(chuàng)建任務(wù)并加入到任務(wù)隊(duì)列中
addTask(new Runnable(){
@Override
public void run() {
//.....
}
});
}
private synchronized void addTask(Runnable runnable){
//將任務(wù)加入到任務(wù)隊(duì)列中
//.......
//發(fā)送消息給后臺(tái)輪詢線程
if(mPoolingThreadHandler==null){
try {
mSemaphorePoolingThreadHandler.acquire();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
mPoolingThreadHandler.sendEmptyMessage(0x110);
}
}
其實(shí),上面核心代碼的主要目的是讓我們的mPoolingThreadHandler的handleMessage方法能夠運(yùn)行在子線程中纤泵,但是骆姐,mPoolingThreadHandler的創(chuàng)建是在后臺(tái)輪詢線程的run方法中,mPoolingThreadHandler的使用是在主線程的loadImage方法中捏题,所以很有可能mPoolingThreadHandler在主線程中使用的時(shí)候處于尚未創(chuàng)建完成的狀態(tài)玻褪,導(dǎo)致程序報(bào)空指針異常。上述代碼使用了Semaphore來解決這個(gè)問題公荧,簡單地講归园,就是我們在主線程中使用mPoolingThreadHandler的時(shí)候,如果發(fā)現(xiàn)mPoolingThreadHandler為空稚矿,會(huì)去請求一個(gè)信號(hào)量,我們在后臺(tái)輪詢線程的run方法中捻浦,一旦mPoolingThreadHandler創(chuàng)建完成晤揣,就釋放一個(gè)信號(hào)量,以此來解決我們主線程和后臺(tái)輪詢線程之間的同步問題朱灿。
不知道大家有沒有這樣的感覺昧识,上面的程序,寫起來好麻煩盗扒。跪楞。。
那有沒有更加簡化的寫法呢侣灶?答案是有的甸祭!有的!褥影!有的3鼗А!凡怎!重要的事情說三遍校焦!
這就要用到我們今天即將介紹的HandlerThread了。
先來看一下HandlerThread在Google官方文檔上的介紹:
官方文檔講得很清楚统倒,HandlerThread是一個(gè)用于啟動(dòng)一個(gè)擁有Looper的新線程的類寨典,該Looper能夠被用來創(chuàng)建Handler,特別注意的是房匆,start()方法仍然必須被調(diào)用耸成。
我們再去看HandlerThread的成員方法报亩,發(fā)現(xiàn)有這樣一個(gè)方法:
該方法會(huì)返回一個(gè)和該線程相關(guān)的Looper對象。如果該線程已經(jīng)被啟動(dòng)墓猎,該方法會(huì)阻塞直至Looper對象被初始化完成捆昏。
我們再去看一下Handler的構(gòu)造方法:
可以看到,Handler有一個(gè)構(gòu)造方法是以Looper 作為參數(shù)的毙沾。
之前在Android異步消息處理機(jī)制深度解析這篇文章中有過這樣的總結(jié):如果創(chuàng)建Handler時(shí)用的是主線程的Looper,則相應(yīng)的handleMessage方法會(huì)運(yùn)行在主線程中骗卜,如果創(chuàng)建Handler時(shí)用的是子線程的Looper,則相應(yīng)的handleMessage方法會(huì)運(yùn)行在子線程中。
好了左胞,知道了這些寇仓,現(xiàn)在我們可以去重寫相冊圖片加載模塊的代碼了,重寫后的代碼如下:
public class ImageLoader {
private HandlerThread mPoolingThread; //后臺(tái)輪詢線程
private Handler mPoolingThreadHandler; //后臺(tái)輪詢線程對應(yīng)的Handler
private ImageLoader(){
mPoolingThread=new HandlerThread("Pooling Thread");
mPoolingThread.start();
mPoolingThreadHandler=new Handler(mPoolingThread.getLooper()){
@Override
public void handleMessage(Message message){
//該方法運(yùn)行在子線程中
//從任務(wù)隊(duì)列中取出一個(gè)任務(wù)烤宙,并交給線程池執(zhí)行
//......
}
};
}
public void loadImage(final String path,final ImageView imageView){
//沒有在LruCache中找到相應(yīng)的圖片
//創(chuàng)建任務(wù)并加入到任務(wù)隊(duì)列中
addTask(new Runnable(){
@Override
public void run() {
//.....
}
});
}
private synchronized void addTask(Runnable runnable){
//將任務(wù)加入到任務(wù)隊(duì)列中
//.......
//發(fā)送消息給后臺(tái)輪詢線程
mPoolingThreadHandler.sendEmptyMessage(0x110);
}
}
雖然我們是在主線程初始化mPoolingThreadHandler遍烦,但由于初始化的時(shí)候用的是子線程mPoolingThread的Looper,所以mPoolingThreadHandler的handleMessage方法是運(yùn)行在子線程中的躺枕,效果和之前的ImageLoader代碼是一模一樣的服猪。
好了,今天對HandlerThread的介紹到這里就要結(jié)束了拐云,相信大家對HandlerThread已經(jīng)有了一個(gè)大體的了解了罢猪,下一篇博客將對HandlerThread的源碼進(jìn)行深入剖析,讓大家對HandlerThread有一個(gè)全面而透徹的理解叉瘩,一起期待吧膳帕!
參考:http://blog.csdn.net/lmj623565791/article/details/47079737