美文网首页
记录-HandlerThread源码分析

记录-HandlerThread源码分析

作者: Manza | 来源:发表于2020-06-06 12:34 被阅读0次

    上文介绍到了IntentService源码分析,其内部维护了一个HandlerThread的线程,今天周末闲来无事,让我看来看下它内部都做了些什么?

    首先回顾下IntentService中HandlerThread的使用

    请看下面的贴图


    image.png

    可以看到,在IntentService的onCreate方法中,实例化了一个HandlerThread,然后调用start()方法启动它,并通过getLooper()方法获取到当前线程的Looper对象,然后new 一个在IntentService中定义的ServiceHandler类。

    1.HandlerThread的定义

    image.png

    从上面这张图中我们可以看到源码对HandlerThread的定义:一个持有Looper对象的线程。这个Looper可以用来创建Hanlder(IntentService中也确实是这样使用的),然后说了一下注意事项:就像普通的Thread类一样,必须要调用HandlerThread内部的start()方法(至于为什么,下面会讲到)。

    2.直接进入正题,贴源码

    package android.os;
    
    import android.annotation.NonNull;
    import android.annotation.Nullable;
    
    /**
     * 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;
        Looper mLooper;
        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() {
            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.
         */
        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;
        }
    
        /**
         * @return a shared {@link Handler} associated with this thread
         * @hide
         */
        @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
         */
        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.
         */
        public boolean quitSafely() {
            Looper looper = getLooper();
            if (looper != null) {
                looper.quitSafely();
                return true;
            }
            return false;
        }
    
        /**
         * Returns the identifier of this thread. See Process.myTid().
         */
        public int getThreadId() {
            return mTid;
        }
    }
    

    可以看到HandlerThread源码并不是很多,主要就是那个几个方法。接下来我们一一剖析。

    • 从构造方法开始说起,从上面的源码中我们看到HandlerThread中有两个构造方法。只有一个参数的构造方法,只接收一个name参数,首先调用的super(name)方法,该参数的作用是指定该线程的名字,然后指定了默认的线程优先级:Process.THREAD_PRIORITY_DEFAULT;有两个参数的构造方法跟上面那个类似,只不过我们可以在创建HandlerThread的时候自己指定线程的优先级。一般情况下我们使用默认的线程优先级就可以了。
    • 结合IntentService中ServiceHandler的使用我们看到在实例化HandlerThread之后,通过thread对象调用了它的start()方法,前面定义中也有提到
     * Note that just like with a regular {@link Thread}, {@link #start()} must still be called.
    

    必须要调用HandlerThread的start()方法。这是什么呢?
    答案很简单,因为我们需要一个Looper对象来实例化Handler()。
    我们看到在实例化ServiceHandler()的时候,我们首先通过调用thread.getLooper()方法拿到了HandlerThread中的Looper对象。没有看过源码的朋友会不会对为什么thread.getLooper()会拿到一个Looper对象疑惑呢?

    • 下面我们看下getLooper()方法源码:


      image.png

      英语比较好的同学通过方法注释其实已经了解该方法的作用了:该方法返回当前线程的Looper对象。如果这个线程没有启动,又或者不管任何原因isAlive()方法返回false,那么这个方法就会返回null。如果线程已经启动,在Looper对象初始化之前线程会一直处于阻塞状态。
      在代码中我们也可以看到,在同步方法中如果isAlive()&&mLooper==null结果为true,那么该线程就会调用wait()方法进入阻塞状态。如果mLooper对象不为null,则直接返回。

    • 看完getLooper()源码之后,我们回想一下,mLooper是在什么时候实例化的呢,对,就是在run()方法中,前面定义中讲到,一定要调用start()方法,它会触发系统调用run()方法,而mLooper就是在该方法中实例化的。
      请看贴图:


      image.png
    • 下面讲一点题外话,分析源码要看要点,注意力不要放在其他与你目前分析不太相关的代码上,如果实在有兴趣,可以先记下随后了解。如果过于深究每行代码的意思反而会顾此失彼,到最后无法把整个思路串联起来,以上都是个人经验(特别聪明的朋友请忽略我这些话)。
    • 我们看到run()方法中主要是调用了Looper.Prepare()来实例化一个Looper对象,然后在同步方法中通过Looper.myLooper()方法来拿到实例化后的Looper对象并赋值给该HandlerThread中的mLooper属性,剩下比较重要的就是调用Looper.loop()方法来轮询消息了。
    • 稍微做下扩展,我们来看下Looper.prepare()方法源码


      image.png

      可以看到prepare()方法中调用了它的重载方法,然后在重载方法中首先通过该线程的sThreadLocal.get()来获取Looper对象,如果对象不为null就会抛出一个”每个线程只能创建一个looper对象“的异常(相信很多人都会遇到过,在我们不清楚源码的时候,确实是会犯一些这样的错误,所以说看源码可以帮我们避开一些坑),如果为null则会new一个Looper对象并通过sThreadLocal的set方法进行保存,以便之后通过get()方法来获取。
      接下来我们看下run方法中调用的Looper.myLooper()方法的源码


      image.png

    代码很简单,就是通过sThreadLocal获取上面我们保存的Looper对象。
    啰嗦了一大堆,至此我们的Looper对象拿到了,前面提到getLooper() 方法在Looper对象没有实例化的时候会调用wait()释放锁并进入阻塞状态。我们看下上面贴图run()中的notifyAll()方法,这个方法的作用就是唤醒阻塞状态的线程。
    至此我们在IntentService的onCreate()方法中通过HandlerTherad的对象调用getLooper()方法获取到了Looper对象,然后就可以用它来实例化Handler对象了。

    3.为什么要使用HandlerThread()?

    方便!
    假如我们要在子线程中使用Handler如何处理呢?
    正常情况下:

    • 创建一个Thread,并在run()方法中做如下处理
    • Looper.prepare()为当前线程初始化Looper对象
    • Looper.myLooper()获取Looper对象
    • 使用获取到的Looper对象来实例化Handler
            Thread {
                Looper.prepare()
                val looper = Looper.myLooper()
                looper?.let {
                    mHandler = Handler(it)
                    Looper.loop()
                }
            }.start()
    

    如果我们使用HandlerThread则只需要以下代码:

    val handlerThread = HandlerThread("handler-thread")
            handlerThread.start()
            mHandler = Handler(handlerThread.looper)
    

    可以看到,Looper.prepare()和Looper.myLooper()都不需要我们自己处理了而是交给了HandlerThread的run方法来处理。

    相关文章

      网友评论

          本文标题:记录-HandlerThread源码分析

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