Android学习--后台线程之Looper、Handler、HandlerThread

来源:互联网 发布:魔兽世界nga数据库 编辑:程序博客网 时间:2024/05/08 05:32

利用AsyncTask获得后台线程适用于那些短暂且较少重复的任务,而当有重复且长时间运行的任务需要后台运作时,我们可以用到LooperHandler,与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相关联。这意味着一个HandlerMessage可能与另一个HandlerMessage存放在同一消息队列中。

消息的目标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如何访问主线程。

主线程是一个拥有HandlerLooper的消息循环。主线程上创建的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方法之后,  //为了保证线程就绪       }       ...}

如上,通过mResponseHandlerThumbnailDownloader能够访问与主线程Looper绑定的Handler。另外,我们也可以返回定制Message给主线程,这里有一种方便的方法,就是利用Handlerpost(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();}
@OverridePublic void onDestroyView(){       super.onDestroyView();       mThumbnailThread.clearQueue();}





0 0
原创粉丝点击