利用AsyncTask获得后台线程适用于那些短暂且较少重复的任务,而当有重复且长时间运行的任务需要后台运作时,我们可以用到Looper,Handler,与HandlerThread。
Android系统中,线程使用的收件箱叫做消息队列(Message Queue)。使用消息队列的线程叫做消息循环(Message Loop)。消息循环会不断循环检查队列上是否有新消息。消息循环由一个线程和一个Looper组成。Looper对象管理着线程的消息队列。在使用Looper时,我们会使用一个HandlerThread类。(主线程也是一个消息循环,因此具有一个Looper。主线程的所有工作都是由其Looper完成的,Looper不断从消息队列中抓取消息,然后完成消息指定的任务。)
消息是Message类的一个实例,包含有好几个实例变量。其中有三个需在实现时定义:
What:用户定义的int型消息代码,用来描述消息;
Obj:随消息发送的用户指定对象;
Target:处理消息的Handler。
要处理消息以及消息指定的任务,首先需要一个消息Handler实例。Handler不仅仅是处理Message的目标(Target),也是创建和发布Message的接口。
Looper拥有Message对象的收件箱,所以Message必须在Looper上发布或读取。而一个Handler仅与一个Looper相关联,一个Message也仅与一个目标Handler相关联。而多个Handler可与一个Looper相关联。这意味着一个Handler的Message可能与另一个Handler的Message存放在同一消息队列中。
消息的目标Handler通常不需要手动设置。理想方式是调用Handler.obtainMessage()方法创建信息并传入其他消息字段,然后该方法会自动完成目标Handler的设置。而此方法也会从公共循环池里获取消息,避免创建新的Message对象。
一旦获得Message,我们就调用sendToTarget()方法将其发送给它的Handler。紧接着Handler会将Message防止在Looper消息队列的尾部。如下代码显示:
Public class ThumbnailDownloader<Token> extends HandlerThread {
...
Private static final int MESSAGE_DOWNLOAD = 0;
Handler mHandler;
Map<Token , String> requestMap =
Collections.synchronizedMap(new HashMap<Token , String>());
@SuppressLint(“HandlerLeak”)
@Override
Protected void onLooperPrepared(){ //该方法在Looper第一次检查消息队列前就调用
mHandler = new Handler() { //在Handler子类中创建Handler实现
@Override
Public void handleMessage(Message msg){
If(msg.what == MESSAGE_DOWNLOAD){
@SuppressWarnings(“unchecked”)
Token token = (Token)msg.obj;
handleRequest(token);
}
}
};
}
Public void queueThumbnail(Token token , String url){
requestMap.put(token , url);
mHandler.obtaninMessage(MESSAGE_DOWNLOAD , token) //该方法会自动完成 //Handler的设置
.sendToTarget(); //将获得的Message发送给它的Handler
}
Private void handleRequest(final Token token){
....
}
}
Public class PhotoGalleryFragment extends Fragment {
ThumbnailDownloader<ImageView> mThumbnailThread;
@Override
Public void onCreate(Bundle savedInstanceState){
...
mThumbnailThread = new ThumbnailDownloader<ImageView>();
mThumbnailThread.start();
mThunbnailThread.getLooper(); //getLooper()方法要在start方法之后, //为了保证线程就绪
}
@Override
Public void onDestroy(){
super.onDestroy();
mThumbnailThread.quit(); //结束线程的quit()方法要在onDestroy()中完成
}
}
下面介绍的是HandlerThread如何访问主线程。
主线程是一个拥有Handler和Looper的消息循环。主线程上创建的Handler会自动与它的Looper相关联。我们可以将主线程上创建的Handler传递给另一线程。传递出去的Handler与创建它 的线程Looper始终保持着联系。因此,任何已传出Handler负责处理的消息都将在主线程的消息队列中处理。所以我们利用Handler的传递既可以实现在主线程上安排后台线程上的任务,也可以在后台线程上安排要在主线程上完成的任务。
方法是在HandlerThread上添加mResponseHandler变量,以存放来自于主线程的Handler。然后构造一个接受Handler的构造方法,最后增加一个用来通信的监听器借口。实现代码如下:
Public class ThumbnailDownloader <Token> extends HandlerThread{ //添加反馈Handler
...
Handler mResponseHandler;
Listener<Token> mListener;
Public interface Listener<Token> {
Void onThumbnailDownloaded( Token token , Bitmap thumbnail );
}
Public void setListener( Listener<Token> listener ){
mListener = listener;
}
Public ThumbnailDownloader( Handler responseHandler ){
mResponseHandler = responseHandler;
}
}
Public class PhotoGalleryFragment extends Fragment { //关联使用反馈Handler
...
@Override
Public void onCreate(Bundle savedInstanceState){
...
mThumbnailThread = new ThumbnailDownloader<ImageView>();
mThumbnailThread = new ThumbnailDownloader<imageView>( new Handler() );
mThumbnailThread.setListener(new ThumbnailDownloader.Listener<ImageView>() {
Public void onThumbnailDownloaded(ImageView imageView , Bitmap thumbnail){
If(isVisible()){
imageView.setImageBitmap(thumbnail);
}
}
});
mThumbnailThread.start();
mThunbnailThread.getLooper(); //getLooper()方法要在start方法之后, //为了保证线程就绪
}
...
}
如上,通过mResponseHandler,ThumbnailDownloader能够访问与主线程Looper绑定的Handler。另外,我们也可以返回定制Message给主线程,这里有一种方便的方法,就是利用Handler的post(Runnable)方法,实现如下:
Public class ThumbnailDownloader<Token> extends HandlerThread {
Private void handleRequest(final Token token){
....
mResponseHandler.post(new Runnable() {
Public void run(){
If(requestMap.get(token) != url)
Return;
requestMap.remove(token);
mListener.onThumbnailDownloaded(token , bitmap);
}
});
}
}
最后,记得添加清理方法清除队列外的所有请求。
Public void clearQueue(){
mHandler.removeMessages(MESSAGE_DOWNLOAD);
requestMap.clear();
}
@Override
Public void onDestroyView(){
super.onDestroyView();
mThumbnailThread.clearQueue();
}