美文网首页Android技术点
Android HandlerThread

Android HandlerThread

作者: 撑起头顶的天 | 来源:发表于2018-02-02 17:09 被阅读8次

    HandlerThread特点

    • HandlerThread本质上是一个线程类,它继承了Thread;
    • HandlerThread有自己的内部Looper对象,可以进行looper循环;
    • 通过获取HandlerThread的looper对象传递给Handler对象,可以在handleMessage方法中执行异步任务。
    • 创建HandlerThread后必须先调用HandlerThread.start()方法,Thread会先调用run方法,创建Looper对象。

    HandlerThread使用步骤

    1.创建实例对象

    HandlerThread handlerThread = new HandlerThread("hanlder_thread");
    

    传入参数的作用主要是标记当前线程的名字,可以任意字符串。

    2.启动HandlerThread线程

    //必须先开启线程
    handlerThread.start();
    

    到此,我们创建完HandlerThread并启动了线程。那么我们怎么将一个耗时的异步任务投放到HandlerThread线程中去执行呢?接下来看下面步骤:
    3.构建循环消息处理机制

        /**
         * 该callback运行于子线程
         */
        class ChildCallback implements Handler.Callback {
            @Override
            public boolean handleMessage(Message msg) {
                //在子线程中进行相应的网络请求
    
                //通知主线程去更新UI
                mUIHandler.sendMessage(msg1);
                return false;
            }
        }
    

    4.构建异步handler

    //子线程Handler
    Handler childHandler = new Handler(handlerThread.getLooper(),new ChildCallback());
    

    第3步和第4步是构建一个可以用于异步操作的handler,并将前面创建的HandlerThread的Looper对象以及Callback接口类作为参数传递给当前的handler,这样当前的异步handler就拥有了HandlerThread的Looper对象,由于HandlerThread本身是异步线程,因此Looper也与异步线程绑定,从而handlerMessage方法也就可以异步处理耗时任务了,这样我们的Looper+Handler+MessageQueue+Thread异步循环机制构建完成。

    HandlerThread原理

    源码:

    /**
     * Handy class for starting a new thread that has a looper. The looper can then be 
     * used to create handler classes. Note that start() must still be called.
     */
    public class HandlerThread extends Thread {
        int mPriority;//线程优先级
        int mTid = -1;
        Looper mLooper;//当前线程持有的Looper对象
        public HandlerThread(String name) {
            super(name);
            mPriority = Process.THREAD_PRIORITY_DEFAULT;
        }
    
        /**
         * Constructs a HandlerThread.
         * @param name
         * @param priority The priority to run the thread at. The value supplied must be from 
         * {@link android.os.Process} and not from java.lang.Thread.
         */
        public HandlerThread(String name, int priority) {
            super(name);
            mPriority = priority;
        }
    
        /**
         * Call back method that can be explicitly overridden if needed to execute some
         * setup before Looper loops.
         */
        protected void onLooperPrepared() {
        }
    

    从源码可以看出HandlerThread继续自Thread,构造函数的传递参数有两个,一个是name指的是线程的名称,一个是priority指的是线程优先级,我们根据需要调用即可。其中成员变量mLooper就是HandlerThread自己持有的Looper对象。onLooperPrepared()该方法是一个空实现,是留给我们必要时可以去重写的,但是注意重写时机是在Looper循环启动前,再看看run方法:

    @Override
    public void run() {
            mTid = Process.myTid();
            Looper.prepare();
            synchronized (this) {
                mLooper = Looper.myLooper();
                notifyAll(); //唤醒等待线程
            }
            Process.setThreadPriority(mPriority);
            onLooperPrepared();
            Looper.loop();
            mTid = -1;
       }
    

    在创建HandlerThread对象后必须调用其start()方法才能进行其他操作,而调用start()方法后相当于启动了线程,也就是run方法将会被调用,而我们从run源码中可以看出其执行了 - Looper.prepare() - 代码,这时Looper对象将被创建,当Looper对象被创建后将绑定在当前线程(也就是当前异步线程),这样我们才可以把Looper对象赋值给Handler对象,进而确保Handler对象中的handleMessage方法是在异步线程执行的。接着将执行代码:

    synchronized (this) {
           mLooper = Looper.myLooper();
           notifyAll(); //唤醒等待线程
       }
    

    这里在Looper对象创建后将其赋值给HandlerThread的内部变量mLooper,并通过notifyAll()方法去唤醒等待线程,最后执行Looper.loop();代码,开启looper循环语句。那这里为什么要唤醒等待线程呢?我们来看看,getLooper方法

    public Looper getLooper() {
     //先判断当前线程是否启动了
       if (!isAlive()) {
           return null;
       }
       // If the thread has been started, wait until the looper has been created.
       synchronized (this) {
           while (isAlive() && mLooper == null) {
               try {
                   wait();//等待唤醒
               } catch (InterruptedException e) {
               }
           }
       }
       return mLooper;
    }
    

    事实上可以看出外部在通过getLooper方法获取looper对象时会先先判断当前线程是否启动了,如果线程已经启动,那么将会进入同步语句并判断Looper是否为null,为null则代表Looper对象还没有被赋值,也就是还没被创建,此时当前调用线程进入等待阶段,直到Looper对象被创建并通过 notifyAll()方法唤醒等待线程,最后才返回Looper对象,之所以需要等待唤醒机制,是因为Looper的创建是在子线程中执行的,而调用getLooper方法则是在主线程进行的,这样我们就无法保障我们在调用getLooper方法时Looper已经被创建,到这里我们也就明白了在获取mLooper对象时会存在一个同步的问题,只有当线程创建成功并且Looper对象也创建成功之后才能获得mLooper的值,HandlerThread内部则通过等待唤醒机制解决了同步问题。

    public boolean quit() {  
           Looper looper = getLooper();  
           if (looper != null) {  
               looper.quit();  
               return true;  
           }  
           return false;  
       }  
    public boolean quitSafely() {  
        Looper looper = getLooper();  
        if (looper != null) {  
               looper.quitSafely();  
               return true;  
           }  
           return false;  
       }  
    

    从源码可以看出当我们调用quit方法时,其内部实际上是调用Looper的quit方法而最终执行的则是MessageQueue中的removeAllMessagesLocked方法(Handler消息机制知识点),该方法主要是把MessageQueue消息池中所有的消息全部清空,无论是延迟消息(延迟消息是指通过sendMessageDelayed或通过postDelayed等方法发送)还是非延迟消息。
      当调用quitSafely方法时,其内部调用的是Looper的quitSafely方法而最终执行的是MessageQueue中的removeAllFutureMessagesLocked方法,该方法只会清空MessageQueue消息池中所有的延迟消息,并将消息池中所有的非延迟消息派发出去让Handler去处理完成后才停止Looper循环,quitSafely相比于quit方法安全的原因在于清空消息之前会派发所有的非延迟消息。最后需要注意的是Looper的quit方法是基于API 1,而Looper的quitSafely方法则是基于API 18的。

    相关文章

      网友评论

        本文标题:Android HandlerThread

        本文链接:https://www.haomeiwen.com/subject/fdyezxtx.html