美文网首页
HandlerThread源码解析与使用

HandlerThread源码解析与使用

作者: Magic旭 | 来源:发表于2019-06-04 14:23 被阅读0次

    HandlerThread类的用法

    前传:如果你还沉醉在于创建普通自线程,沉迷于创建线程通信的Handler,不如来试试HandlerThread类的使用。其内部的源码也非常简单,只要你稍微看过Handler,Thread,Looper内部的实现流程就能把HandlerThread类的源码看懂了。

    HandlerThread构造函数
    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;
        }
    
    1. 构造函数(String name)只需要传入线程的名称,就会为你创建对应名为name的自线程,其权重为0。
    2. 构造函数(String name, int priority)这需要创建者输入线程名字和权重,其实和👆的构造方法区别不大。
      注意:权重越高,线程优先级越高,越容易被执行(相对而言)。
    子线程如何创建
    1. 构造方法里面调用Thread类的super(name)方法,内部实现和我们平时构建子线程没啥区别,区别在于调用了不同的构造方法,曾经年少的我们是啥都不传的,所以Thread.name = "Thread-" + nextThreadNum();现在的我们成长了,需要给它们定义一个感(中)人(二)的名字了。两者对应的构造函数可以看下面的源码。
     /**
         * Allocates a new {@code Thread} object. This constructor has the same
         * effect as {@linkplain #Thread(ThreadGroup,Runnable,String) Thread}
         * {@code (null, null, gname)}, where {@code gname} is a newly generated
         * name. Automatically generated names are of the form
         * {@code "Thread-"+}<i>n</i>, where <i>n</i> is an integer.
         */
        public Thread() {
            init(null, null, "Thread-" + nextThreadNum(), 0);
        }
    
    /**
         * Allocates a new {@code Thread} object. This constructor has the same
         * effect as {@linkplain #Thread(ThreadGroup,Runnable,String) Thread}
         * {@code (null, null, name)}.
         *
         * @param   name
         *          the name of the new thread
         */
        public Thread(String name) {
            init(null, null, name, 0);
        }
    
    Handler的创建
    1. 内部会在你需要使用Handler在子线程通信的时候生成实例,可以看到Handler的构造函数中传入了当前子线程的Looper;平时我们在子线程生成Handler是不在构造函数中传递任何参数的,但其实Handler内部也通过Looper.myLooper来获取当前线程的looper,具体源码如下。
    //不传任何构造参数的Handler生成方法
    public Handler(Callback callback, boolean async) {
            if (FIND_POTENTIAL_LEAKS) {
                final Class<? extends Handler> klass = getClass();
                if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                        (klass.getModifiers() & Modifier.STATIC) == 0) {
                    Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                        klass.getCanonicalName());
                }
            }
            //这里获取Handler所在线程的Looper
            mLooper = Looper.myLooper();
            if (mLooper == null) {
                throw new RuntimeException(
                    "Can't create handler inside thread " + Thread.currentThread()
                            + " that has not called Looper.prepare()");
            }
            mQueue = mLooper.mQueue;
            mCallback = callback;
            mAsynchronous = async;
        }
    
    //HandlerThread返回Handler的方法
    /**
         * @return a shared {@link Handler} associated with this thread
         * @hide
         */
        @NonNull
        public Handler getThreadHandler() {
            if (mHandler == null) {
                mHandler = new Handler(getLooper());
            }
            return mHandler;
        }
    
    getLooper方法
    1. getLooper方法内部是判断当前HandlerThread的mLooper变量是否被初始化了,如果没有就调用wait进行挂起。如果挂起了什么时候唤醒了?
    2. 唤醒是在run方法里面,首先会执行Looper.peapare方法,储存 ThreadLocalMap 里面,key是当前线程,value是looper实例。
    @Override
        public void run() {
            mTid = Process.myTid();
            Looper.prepare();
            synchronized (this) {
                mLooper = Looper.myLooper();
                notifyAll();
            }
            Process.setThreadPriority(mPriority);
            onLooperPrepared();
            Looper.loop();
            mTid = -1;
        }
    
        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;
        }
    

    注意:可以看出,其内部还是通过Looper.prepare()方法创建Handler的实例,只不过高度封装起来,对开发者来说是透明化的。开发者不需要再到自己的Thread里面生成Handler了。

    总结

    1. 如果你需要发送某些特殊事件回到子线程去处理,我建议直接使用HandlerThread。不大建议使用传统的自定义Thread里面在new Handler,毕竟google官网都已经封装好了,为啥不用呢?(来自一名懒惰程序员的呐喊)
    2. 用传统方式很容易遗忘了new Handler要在Looper.pepare之后执行,要不然会抛出异常。


      image.png

    相关文章

      网友评论

          本文标题:HandlerThread源码解析与使用

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