HandlerThread

话说某一天去游族面试,被问到知不知道HanderThread,当时就懵逼了,梗了半天回答不出来,没有了解过...
结果,结果成功被刷~
其实翻了下源码发现挺简单的,这玩意其实就是一个Thread子类并且帮你创建一个Looper并开了Looper循环,我们之前一般用Handler都是习惯在UI线程中去new个Handler然后在子线程中去sendMessage这样就可以子线程执行耗时操作然后在UI线程中跟新UI。
但有时我们可能需要在子线程中做耗时操作然后在子线程中跟新UI,别跟我说不可能,SurfaceView?
这样的话我们的Looper就不能在UI线程中去接收Message消息了。
这种操作一般有几种做法.
1.我们在子线程中利用while或者for循环重复去做耗时跟新操作
2.在子线程中创建一个Looper然后调用Looper prapre() 和 loop()遍历和发送消息

第一种操作先不去探究,而HandlerThread就是对第二种方式简易的封装,让实现变的更容易一些

我们看看怎么使用的

        HandlerThread handlerThread=new HandlerThread("new_thread1");
        handlerThread.start();
        Handler handler=new Handler(handlerThread.getLooper()){
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
            }
        };

是不是感觉简单到不可思议居然就这么几行代码就可以了?不错这样就算Handler在主线程中new出来的,那么它处理消息也在我们创建的new_thread1这个子线程中去执行,如果你也感觉到很好奇那么我们就分析一下源码,在分析之前,先回忆一下Handler Looper Message之间的知识,简单回忆下,我们知道,Handler之所以可以在UI线程中处理消息因为它在UI线程中new出来的,在new出来过程中会从UI线程中的threadlocal中取出UI线程创建Looper对象存于成员变量,这样不管我们在哪个子线程调用handler发送Message那么都是发送到UI线程中Looper中的MessageQueue队列中,这个Looper是在UI线程中调用loop()阻塞遍历队列消息的,接受到消息后调用message里的handler的dispatchMessage()去处理消息所以一定发生在ui线程中

  public Handler(Looper looper) {
        this(looper, null, false);
    }
    public Handler(Looper looper, Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

而Handler有个构造接收一个Looper参数最终会调用三参构造,这个构造中会把Looper赋值成员变量,我们在上面操作HandlerThread时是获取它的Looper然后赋值给Handler成员变量的,那么它的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;
    }

看到了吧,其中有个 onLooperPrepared();方法是个空实现方法我们可以重写它在loop()方法调用之前进行一些准备工作,其中有个小知识点需要注意下,虽然我们开始调用了start()开启了一个新线程并且在run()方法中创建了Looper但是由于线程之间的执行次序是不确定的,所以我们需要等子线程中的Looper对象完全创建完毕后才能通过getLooper()拿到这个对象,所以在getLooper()方法中进行了一个判空wait()状态

    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()调用完毕Looper对象创建完毕调用notifyAll唤醒了前面wait()的线程

推荐阅读更多精彩内容