阅读 94

HandlerThread源码解析

假设我们从没接触过HandlerThread,单从Handler和Thread这两个词我们大概能猜得出这个类是Handler+Thread。没错这个类就是一个具备消息机制的线程类。

为什么需要这个类

相信大家在刚开始学习Handler的时候都会被告知不能在子线程中直接创建Handler对象,例如如果你运行如下代码程序就会直接奔溃,然后能看到一条错误日志:Can't create handler inside thread Thread[Thread-2,5,main] that has not called Looper.prepare()。

new Thread() {
     @Override
     public void run() {
         super.run();
         Handler handler = new Handler();
     }
 }.start();
复制代码

从这个错误日志我们就能知道需要在创建Handler对象之前调用下Looper.prepare(),当然要让子线程的消息机制能正常工作还需要再调用下Looper.loop()。而HandlerThread主要就是帮我们把这些事情给做了,让我们可以无需手动设置Looper就能使用Handler。

功能

既然HandlerThread已经帮我们初始化好了Looper,那我们就可以在创建HandlerThread之后创建Handler对象,通过该对象发送消息,然后在子线程处理耗时操作。例如

HandlerThread handlerThread = new HandlerThread("test");
handlerThread.start();
Handler threadHandler = new Handler(handlerThread.getLooper()) {
    @Override
    public void handleMessage(@NonNull Message msg) {
        super.handleMessage(msg);
        switch (msg.what) {
            case 1:
                //做一些耗时操作
                break;
        }
    }
};
threadHandler.sendEmptyMessage(1);
复制代码

源码解析

聊完了HandlerThread的背景和功能,那我们就深入源码来看看具体是怎么实现的,是不是只加了Looper.preare()和Looper.loop()这两行代码。 首先HandlerThread是一个Thread,所以自然而要做的事情就是继承Thread然后实现最核心的run()方法。

构造函数

就两个构造函数,参数就是设置线程名和线程的优先级,其他没什么好说的。

public HandlerThread(String name) {
    super(name);
    mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
public HandlerThread(String name, int priority) {
    super(name);
    mPriority = priority;
}
复制代码

run()方法

接着我们就要看最核心的方法run();

    @Override
    public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }
复制代码

看这个源码是不是很熟悉,我们看到了Looper.prepare()和Looper.loop()两个最关键的代码。如果不考虑其他问题,其实这两行代码就足够了。那么疑问来了,中间的synchronsized代码块和onLooperPrepared()代码是在干啥用的?
上面在讲使用HandlerThread时,我们在创建Handler时调用了HandlerThread.getLooper()方法,这个函数的功能就是返回子线程的Looper对象,但子线程的Looper是在run()方法中初始化的,那就存在一个情况是HandlerThread使用者创建了一个HandlerThread对象但还没启动线程也就是没调用start()方法之前就去调用了getLooper。如果不做处理的话那么使用者将得到一个null对象。自然,就得在getLooper()中做些处理。查看源码可以看到如果还未初始化Looper对象就会挂起线程直到Looper初始化完成。这样在run()方法中初始化完成之后就会被唤醒。

    public Looper getLooper() {
   .......省略一些判断.......
        synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }
复制代码

而onLooperPrepared()方法就是一个空方法,看注释可以直到就在一个在Looper初始化完成后的一个回调,我们可以在继承HandlerThread类时去实现这个方法,比如创建一个Handler对象。

/**
     * Call back method that can be explicitly overridden if needed to execute some
     * setup before Looper loops.
     */
    protected void onLooperPrepared() {
    }
复制代码

其他方法

分析完核心方法之后,我们看到还有一些方法:

  • getThreadHandler,获取一个和当前线程的共享Handler对象
public Handler getThreadHandler() {
        if (mHandler == null) {
            mHandler = new Handler(getLooper());
        }
        return mHandler;
    }
复制代码
  • quit()和quitSafely(),这两个方法的区别就在looper.quit()和looper.quitSafely()。这两个方法的区别是quit会马上结束消息处理功能,不管此刻还有没有消息没处理,而quitSafely会相对温和一点,它依然会此刻需要处理的消息,但不会再处理那些延迟消息,比如通过postDelayed发送的消息。
    public boolean quit() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quit();
            return true;
        }
        return false;
    }
    public boolean quitSafely() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quitSafely();
            return true;
        }
        return false;
    }
复制代码

总结

HandlerThread在run方法中初始化Looper,然后对一些异常情况做了一些处理,至于功能,用HandlerThread类中的注释来总结就是:

  • 有Looper的Thread;
  • 可以用这个Looper去创建Handler对象;
  • 像普通Thread一样,记得调用start()方法;
关注下面的标签,发现更多相似文章
评论