美文网首页
Android中HandlerThread类【源码分析】

Android中HandlerThread类【源码分析】

作者: 千夜零一 | 来源:发表于2021-06-01 12:14 被阅读0次

    HandlerThread类

    应用场景:

    Android中特有的,在子线程使用Handler的类,内部封装了Looper操作,调用start()方法即可。

    具备的特征:

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

    【源码分析】:HandlerThread类

    /**
     * A {@link Thread} that has a {@link Looper}.
     * The {@link Looper} can then be used to create {@link Handler}s.
     * <p>
     * Note that just like with a regular {@link Thread}, {@link #start()} must still be called.
     */
    public class HandlerThread extends Thread {
        int mPriority;   //---线程优先级---
        int mTid = -1;   //---线程id---
        Looper mLooper;  //---当前线程持有的Looper对象---
        private @Nullable Handler mHandler;
    
        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() {  //---空实现方法---
        }
    
        @Override
        public void run() {  //---run方法,准备Looper对象,设置线程优先级,让Looper轮训。---
            mTid = Process.myTid();
            Looper.prepare();
            synchronized (this) {
                mLooper = Looper.myLooper();
                notifyAll();   //等待唤醒线程
            }
            Process.setThreadPriority(mPriority);
            onLooperPrepared();
            Looper.loop();
            mTid = -1;
        }
        
        /**
         * This method returns the Looper associated with this thread. If this thread not been started
         * or for any reason isAlive() returns false, this method will return null. If this thread
         * has been started, this method will block until the looper has been initialized.  
         * @return The looper.
         * 此方法返回与此线程关联的 Looper。如果此线程未启动
         * 或由于任何原因 isAlive() 返回 false,此方法将返回 null。如果这个线程已启动,此方法将阻塞,直到循环器初始化。
         * @return 循环器。
         */
        public Looper getLooper() {  //---获取Looper对象---
            if (!isAlive()) {   //线程未启动
                return null;
            }
            
            // If the thread has been started, wait until the looper has been created.
            synchronized (this) {  //加一个同步锁
                while (isAlive() && mLooper == null) {  //当线程未启动&&Looper对象为空
                    try { 
                        wait();   //线程处于等待状态
                    } catch (InterruptedException e) {
                    }
                }
            }
            return mLooper;
        }
    
        /**
         * @return a shared {@link Handler} associated with this thread
         * @hide
         * 返回与此线程关联的共享的Handler对象
         */
        @NonNull
        public Handler getThreadHandler() {
            if (mHandler == null) {
                mHandler = new Handler(getLooper());
            }
            return mHandler;
        }
    
        /**
         * Quits the handler thread's looper.
         * <p>
         * Causes the handler thread's looper to terminate without processing any
         * more messages in the message queue.
         * </p><p>
         * Any attempt to post messages to the queue after the looper is asked to quit will fail.
         * For example, the {@link Handler#sendMessage(Message)} method will return false.
         * </p><p class="note">
         * Using this method may be unsafe because some messages may not be delivered
         * before the looper terminates.  Consider using {@link #quitSafely} instead to ensure
         * that all pending work is completed in an orderly manner.
         * </p>
         *
         * @return True if the looper looper has been asked to quit or false if the
         * thread had not yet started running.
         *
         * @see #quitSafely
         * 【译文:】
         * 退出处理程序线程的循环程序。
         * <p>
         * 导致处理程序线程的循环程序终止而不处理任何
         * 消息队列中的更多消息。
         * </p><p>
         * 任何在 Looper 被要求退出后向队列发布消息的尝试都将失败。
         * 例如,{@link Handler#sendMessage(Message)} 方法将返回 false。
         * </p><p class="note">
         * 使用此方法可能不安全,因为某些消息可能无法传递
         * 在 Looper 终止之前。考虑使用 {@link #quitSafely} 来确保
         * 所有未完成的工作有序完成。
         * </p>
         *
         * @return 如果 Looper Looper 被要求退出,则为 True,否则为 false
         * 线程尚未开始运行。
         *
         * @see #quitSafely
         */
        public boolean quit() {
            Looper looper = getLooper();
            if (looper != null) {
                looper.quit();
                return true;
            }
            return false;
        }
    
        /**
         * Quits the handler thread's looper safely.
         * <p>
         * Causes the handler thread's looper to terminate as soon as all remaining messages
         * in the message queue that are already due to be delivered have been handled.
         * Pending delayed messages with due times in the future will not be delivered.
         * </p><p>
         * Any attempt to post messages to the queue after the looper is asked to quit will fail.
         * For example, the {@link Handler#sendMessage(Message)} method will return false.
         * </p><p>
         * If the thread has not been started or has finished (that is if
         * {@link #getLooper} returns null), then false is returned.
         * Otherwise the looper is asked to quit and true is returned.
         * </p>
         *
         * @return True if the looper looper has been asked to quit or false if the
         * thread had not yet started running.
         * 【译文:】
         * 安全地退出处理线程的looper。
         * <p>
         * 使处理程序线程的循环程序在所有剩余消息后立即终止
         * 已处理的消息队列中已到期的消息。
         * 将不会发送具有未来到期时间的待处理延迟消息。
         * </p><p>
         * 任何在 Looper 被要求退出后向队列发布消息的尝试都将失败。
         * 例如,{@link Handler#sendMessage(Message)} 方法将返回 false。
         * </p><p>
         * 如果线程尚未启动或已完成(即如果
         * {@link #getLooper} 返回 null),然后返回 false。
         * 否则,looper 会被要求退出并返回 true。
         * </p>
         *
         * @return 如果 Looper Looper 被要求退出,则为 True,否则为 false
         * 线程尚未开始运行。
         */
        public boolean quitSafely() {
            Looper looper = getLooper();
            if (looper != null) {
                looper.quitSafely();
                return true;
            }
            return false;
        }
    
        /**
         * Returns the identifier of this thread. See Process.myTid().
         * 返回此线程的标识符。请参阅 Process.myTid()。
         */
        public int getThreadId() {
            return mTid;
        }
    }
    

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

    HandlerThread的常规使用中分析过,在创建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方法。

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


    注意:

    这里的getLooper方法,是指多个线程使用同一把锁,当子线程A并没有创建Looper对象时,为了不使getLooper()返回null,因此让持有的Looper对象为空的子线程先wait()等待,然后子线程B执行到run方法时,创建了Loopter对象,此时执行

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

    notifyAll()这个方法去唤醒子线程A。

    问题:为什么子线程A执行了HandlerThread.start()方法,使用getLooper方法还是拿不到Looper对象呢?

    这是因为可能A调用了start()方法后,没能立即进行run()方法中的Looper获取,因为线程创建需要时间,此时getLooper会进行wait()

    而子线程B调用了start方法后,同步锁代码块的Looper对象进行了赋值操作,然后唤醒争夺该资源的所有线程,此时子线程A被B唤醒,Looper不为空,子线程也处于Alive状态,因此不会再处于wait()状态。

    参考:Android 多线程之HandlerThread 完全详解

    相关文章

      网友评论

          本文标题:Android中HandlerThread类【源码分析】

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