• 安卓权威编程指南-笔记(第24章 Looper Handler 和 HandlerThread)


    AsyncTask是执行后台线程的最简单方式,但它不适用于那些重复且长时间运行的任务。

    1. Looper

    Android中,线程拥有一个消息队列(message queue),使用消息队列的线程叫做消息循环(message loop)。消息循环会循环检查队列上是否有新消息。

    消息循环由线程和looper组成,Looper对象管理着线程的消息队列。

    主线程就是个消息循环,因此也拥有Looper,主线程的所有工作都是由其looper完成的,looper不断的从消息队列中抓去消息,然后完成消息指定的任务。

    2. Message

    消息是Message类的一个实例,它有好几个实例变量,其中有三个需要在实现时定义。

    1.What:用户定义的int型消息代码,用来描述消息。

    2.obj:随消息发送的用户指定对象。

    3.target: 处理消息的Handler。

    3. Handler

    Message的目标(target)是Handler类的一个实例,Handler可看作message handler的简称,创建Message时,它会自动与一个handler相关联,message待处理时,Handler对象负责触发消息处理事件、

    Handler不仅仅是处理Message的目标,也是创建和发布Message的接口。

    4. 关系

    Looper拥有Message收件箱,所以Message必须在Looper上发布或处理。为与Looper协同工作,Handler总是引用着它。

    一个Handler仅与一个Looper相关联,一个Message也仅于一个目标Handler(也称作Message目标)相关联。Looper拥有整个Message队列,多个Message可以引用同一目标Handler。

    多个Handler也可与一个Looper相关联,这意味着一个Handler的Message可能与另一个Handler的Message存放在同一消息队列中。

    5. 使用Handler 

    通常不需要手动设置消息的目标Handler。创建信息时,调用Handler.obtainMessage()方法。当传入其他消息字段给他时,该方法会自动设置目标给Handler对象。

    为避免创建新的Message对象,Handler.obtainMessage()方法会从公共循环吃获取消息。  

    一旦取得Message,就可以调用sendToTarget()方法将其发送给它的Handler,然后Handler会将这个Message放置在Looper消息队列的尾部。

    Looper取得消息队列中的特定消息后,会将它发送给消息目标去处理。消息一般是在目标的Handler.handleMessage()实现方法中进行处理。

    6. HandlerThread

    HandlerThread类帮我们完成了建立looper的过程,只要继承它就能省去一些工作。

    public class ThumbnailDownloader<T> extends HandlerThread {
        private static final String TAG = "ThumbnailDownloader";
        private static final int MESSAGE_DOWNLOAD = 0; //标识下载请求
        private Boolean mHasQuit = false;
        private Handler mRequestHandler; //存储对Handler的引用,这个Handler负责在ThumbnailDownloader后台线程上管理下载请求消息队列。这个Handler也负责从消息队列里取出并处理下载请求消息。
    
        //onLooperPrepared()在Looper首次检查消息队列之前调用的。
        @Override
        protected void onLooperPrepared(){
            mRequestHandler = new Handler(){
                @Override
                public void handleMessage(Message msg){  //队列中的下载消息取出并可以处理时,就会触发调用Handler.handleMessage()方法。
                    //处理操作
                }
            };
        }
    
        public void queueThumbnail(T target, String url) {
     //当传入其他消息字段给它时,该方法会自动设置目标给Handler对象(obtainMessage)
                //sendToTarget()方法将Message发送给它的Handler,然后Handler会将这个Message放置在Looper消息队列的尾部。
                   mRequestHandler.obtainMessage(MESSAGE_DOWNLOAD,target).sendToTarget();
        }
        public void clearQueue(){
            mRequestHandler.removeMessages(MESSAGE_DOWNLOAD);
        }
        
    }

    主线程中这样调用:

    mThumbnailDownloader = new ThumbnailDownloader<>(responseHandler);
    mThumbnailDownloader.start();
    mThumbnailDownloader.getLooper(); //在start()方法之后调用getLooper()方法是一种保证线程就绪的处理方式。可以避免潜在竞争。
    // 在需要的时候调用
    mThumbnailDownloader.queueThumbnail(holder, url);

    7.线程交互

    主线程现在能够适时调用这个线程的方法,用于下载图片了。但是还存在一个问题,那就是下载线程下载完一个任务以后如何更新视图呢?我们知道 UI 只能在主线程里更新,所以我们采用在主线程里声明一个 Handler,传递给下载线程,让下载线程在下载完成后在主线程执行更新操作。因为不能直接引用主线程的方法,故而在这里用到了回调。

    7.1下载线程中:

    // ThumbnailDownloader,也就是下载线程中
    
    // 成员声明
    private Handler mResponseHandler;
    private ThumbnailDowloadListener<T> mThumbnailDownloadListener;
    
    // 回调接口
    public interface ThumbnailDowloadListener<T> {
    /*
             * 图片下载完成,可以交给UI去显示时,接口中的方法就会被调用。
             * 会使用这个方法把处理已下载图片的任务代理给另一个类(PhotoGalleryFragment),这样ThumbnailDownloader就可以把下载结果传给其他视图对象。
             */
        void onThumbnailDownloaded(T target, Bitmap thumbnail);
    }
    
    public void setThumbnailDownloaderListener(ThumbnailDowloadListener<T> listener) {
        mThumbnailDownloadListener = listener;
    }
    
    // 通过构造函数传递主线程的 Handler
    public ThumbnailDowloader(Handler responseHandler) {
        super(TAG);
        mResponseHandler = responseHandler;
    }

    这样,主线程通过调用这些方法,就能够让下载线程获取到主线程的 Handler 和回调接口实例。

    7.2主线程中

    // 成员声明
    private ThumbnailDowloader<PhotoHolder> mThumbnailDownloader;
    
    // 传递实例给下载线程
    // 这个 Handler 在主线程中建立,所以是和主线程 Looper 相关联的
    Handler responseHandler = new Handler(); 
    mThumbnailDownloader = new ThumbnailDowloader<>(responseHandler);
    mThumbnailDownloader.setThumbnailDownloaderListener(
        new ThumbnailDowloader.ThumbnailDowloadListener<PhotoHolder>() {
            @Override
           public void onThumbnailDownloaded(PhotoHolder target, Bitmap thumbnail) {
                Drawable drawable = new BitmapDrawable(getResources(), thumbnail);
                target.bindDrawable(drawable);
            }
        }
    );

    8.线程交互

    现在,通过 mResponseHandler,下载线程能够访问与主线程 Looper 绑定的 Handler。同时,还有 ThumbnailDownloadListener 使用返回的 Bitmap 执行 UI 更新操作。具体来说, 就是通过 onThumbnailDownloaded 实现,使用新下载的 Bitmap 来设置 PhotoHolder 的 Drawable。 
    和在下载线程上把下载图片的请求放入消息队列类似,我们也可以返回定制 Message 给主线程,要求显示已下载图片。不过,这需要另一个 Handler 子类,以及一个 handleMessage(…) 覆盖方法。方便起见,我们转而使用另一个方便的 Handler 方法——post(Runnable)。

    mResponseHandler.post(new Runnable() {
        @Override
        public void run() {
            if (mRequestMap.get(target) != url ||
                    mHasQuit) {
                return;
            }
    
            mRequestMap.remove(target);
            mThumbnailDownloadListener.onThumbnailDownloaded(target, bitmap);
        }
    });

    在这里,新建的 Runnable 对象会被当成 Message 的回调方法,直接执行 run() 方法,所以相当于发送一个消息,里面写明了怎么做,而不是把对象和消息类型发给 Handler,让 Handler 决定怎么做。

  • 相关阅读:
    Map集合的四种遍历
    java 请求 google translate
    Linux 内核初级管理
    Linux GRUB
    Linux 系统启动流程
    Linux 任务计划 crontab
    Linux 进程管理工具
    Linux sudo实作
    Linux 进程管理
    Linux 网络配置命令:ip、ss
  • 原文地址:https://www.cnblogs.com/chase1/p/7209441.html
Copyright © 2020-2023  润新知