详解HandlerThread

作者: Chase_stars | 来源:发表于2019-05-03 15:40 被阅读16次

    不达成功誓不休 — 程端礼

    写在前面

    “送走四月,喜迎五月”,今年的五一小长假简直舒服到了极致,牺牲两个周六换来的四天假期。是时候出去浪了, 打开微博发现不妙,吓得我赶紧躺床上又睡了一觉...

    入职后经常加班,仅有的私人时间也没有心思学习。不过我还是告诉自己在忙也不能忘了充实自己。趁着今天有兴致,阅读了一下HandlerThread的源码。

    用法

    首先,我们来了解一下HandlerThread。

    Q:什么是HandlerThread?
    A:HandlerThread继承自Thread,可以说其是一个线程。并且有一个Looper对象进行消息循环。

    接下来,我们看一下HandlerThread是如何执行异步任务的。

    // 创建HandlerThread对象,参数为该线程的Name
    HandlerThread handlerThread = new HandlerThread("work");
    // 调用HandlerThread的start()函数开启线程
    handlerThread.start();
    
    // 通过调用HandlerThread的getLooper()函数得到Looper对象,并创建Handler用于执行异步任务
    Handler workHandler = new Handler(handlerThread.getLooper(), new WorkHandlerCallback());
    
    // 在主线程中创建Handler,用于更新UI(主线程中有一个Looper)
    Handler uiHandler = new Handler(new UIHandlerCallback());
    
    // 发送一个异步任务的消息
    Message msg = workHandler.obtainMessage();
    msg.what = 0x01;
    workHandler.sendMessage(msg);
    
    private class WorkHandlerCallback implements Handler.Callback {
    
           @Override
           public boolean handleMessage(Message msg) {
             switch (msg.what) {
                    case 0x01:
                        // 开始执行异步任务
                        int sum = 0;
                        for (int i = 0 ; i < 100 ; i ++) {
                            sum += i;
                        }
                        // 异步任务执行成功后,告知UI线程更新
                        Message message = mUiHandler.obtainMessage();
                        message.what = 0x02;
                        message.obj = sum + "";
                        mUiHandler.sendMessage(message);
                        break;
                        default:
                            break;
                }
               return true;
           }
    }
    
    private class UIHandlerCallback implements Handler.Callback {
    
           @Override
           public boolean handleMessage(Message msg) {
             switch (msg.what) {
                    case 0x02:
                        // 更新UI
                        textView.setText((String) msg.obj);
                        // 异步任务做完以后,不再需要HandlerThread,就调用quit()函数退出
                        handlerThread.quit();
                        break;
                        default:
                            break;
                }           return false;
           }
    }
    

    以上就是HandlerThread的基本用法,是不是很简单呢,通过简单的几步就可以在异步线程中做耗时任务,再也不用担心主线程阻塞了。

    注意:

    • HandlerThread也是一个线程,想要其工作就一定要调用start()函数,而且Looper对象是在run()函数中创建的,只有run()函数执行了,才会创建Looper对象进行消息循环。
    • 通过HandlerThread的getLooper()创建的Handler只能用来执行异步任务,因为它不在主线程中,无法进行UI更新,适合做一些耗时任务。

    源码

    学会了使用HandlerThread,那么就不想知道它的内部是如何实现的吗?反正我是挺想的,也不知道你想不想,咱也不知道,咱也不敢问,有兴趣的童鞋就和我一起探其究竟吧。

    public class HandlerThread extends Thread {
        // 线程优先级
        int mPriority;
        // 线程id
        int mTid = -1;
        // 该线程所持有的Looper对象
        Looper mLooper;
        // 线程内部的Handler,可以通过getThreadHandler()函数直接获取使用
        private @Nullable Handler mHandler;
        
        // 拥有一个参数的构造函数
        // 传入参数为线程名称,具有默认线程优先级
        public HandlerThread(String name) {
            super(name);
            mPriority = Process.THREAD_PRIORITY_DEFAULT;
        }
        
        // 拥有两个参数的构造函数
        // 传入的参数name是线程名称,参数priority是线程优先级(线程优先级详见Process类)
        public HandlerThread(String name, int priority) {
            super(name);
            mPriority = priority;
        }
        
        // 该函数为空实现,子类可以重写该函数,在Looper进行消息循环之前调用
        protected void onLooperPrepared() {
        }
    
        // 调用该线程的start()函数后,run()函数会被执行
        // run()函数可以说是HandlerThread的核心,该函数内部会创建Looper进行消息循环
        @Override
        public void run() {
            // 获取该线程的id
            mTid = Process.myTid();
            // 为该线程创建Looper
            Looper.prepare();
            // 通过持有同步锁机制得到该线程的Looper对象
            // 然后调用notifyAll()函数通知getLooper()函数Looper对象已经创建完成。
            synchronized (this) {
                // 获取该线程的Looper对象
                mLooper = Looper.myLooper();
                // 唤醒在当前对象监视器上等待的所有线程
                notifyAll();
            }
            // 设置线程优先级
            Process.setThreadPriority(mPriority);
            // 调用onLooperPrepared()函数,子类可以在消息循环之前做一些准备工作
            onLooperPrepared();
            // 开始消息循环
            Looper.loop();
            mTid = -1;
        }
        
        // 获取该线程的Looper对象
        public Looper getLooper() {
            // 如果该线程不是isAlive,则直接返回null
            if (!isAlive()) {
                return null;
            }
            
            // 通过持有同步锁机制判断当前是否创建了Looper对象
            // 如果该线程没有start,即还没有创建Looper对象
            // 则调用wait()函数,使当前对象上的线程进入等待,就是等待run()函数中的notifyAll()函数执行
            synchronized (this) {
                while (isAlive() && mLooper == null) {
                    try {
                        wait();
                    } catch (InterruptedException e) {
                    }
                }
            }
            return mLooper;
        }
    
        // 可以直接获取一个Handler对象
        @NonNull
        public Handler getThreadHandler() {
            if (mHandler == null) {
                // 创建Handler实例,参数为该线程的Looper
                mHandler = new Handler(getLooper());
            }
            return mHandler;
        }
    
        // 退出消息循环,效率高,但不是线程安全的
        public boolean quit() {
            Looper looper = getLooper();
            if (looper != null) {
                // 调用Looper对象的quit()函数退出消息循环,
                // 内部调用的是MessageQueue的quit(boolean safe)函数,传入参数为false
                looper.quit();
                return true;
            }
            return false;
        }
    
        // 退出消息循环,效率低,但是线程安全的
        public boolean quitSafely() {
            Looper looper = getLooper();
            if (looper != null) {
                // 调用Looper对象的quitSafely()函数退出消息循环
                // 内部调用的是MessageQueue的quit(boolean safe)函数,传入参数为true
                looper.quitSafely();
                return true;
            }
            return false;
        }
    
        // 获取当前线程id
        public int getThreadId() {
            return mTid;
        }
    }
    

    总结

    分析了HandlerThread的源码后,总结为以下几点:

    • HandlerThread有两个构造函数,分别是线程名称或线程名称和线程优先级。
    • HandlerThread的本质就是一个线程,但不同于线程,它的核心就是run()函数,它在run()函数内部创建一个Looper对象进行消息循环,使通过该Looper创建的Handler实例运行在该线程中。为了避免getLooper()获取到的Looper对象为空,采用了同步锁机制,即在getLooper()函数中若mLooper为null,则让当前对象上的线程进入等待,直到run()函数中创建好Looper对象后,唤醒在当前对象监视器上等待的所有线程,即getLooper()函数继续执行。
    • HandlerThread内部提供了一个Handler,其创建方式也是通过getLooper()函数获取该线程的Looper对象进行创建,不过该Handler只有在外部调用getThreadHandler()时才会进行创建。
    • HandlerThread有两种退出方式,一种不是线程安全的,但是效率高;一种是线程安全的,但是效率低;其最终调用的都是MessageQueue的quit(boolean safe)函数。
    • HandlerThread和Thread一样,都是调用start()函数开启线程。
    • HandlerThread的构造函数需要传入线程名称,而Thread的构造函数则不需要。
    • 继承自Thread的子类需要重写其run()函数,在其中执行任务;而HandlerThread的子类可以重写 onLooperPrepared()函数,在创建Looper对象之前做准备工作。

    相关文章

      网友评论

        本文标题:详解HandlerThread

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