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

本文介绍了在Android中,如何利用Looper、Handler和HandlerThread进行后台线程的长时间任务处理。当需要处理重复且长期运行的任务时,可以使用Looper管理的消息队列和HandlerThread。主线程内置了一个Looper,而Handler负责处理Message。HandlerThread提供了一个可以在其中创建Looper的后台线程,允许通过Handler在不同线程间通信。通过HandlerThread和Handler的配合,可以在后台安排任务并在主线程中执行,反之亦然。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

利用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();
}
@Override
Public void onDestroyView(){
       super.onDestroyView();
       mThumbnailThread.clearQueue();
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值