谈谈Android源码——HandlerThread

2021-02-09  本文已影响0人  CoderCyl

HandlerThread 介绍

HandlerThread 继承自 Thread,所以它本质上还是一个线程。那么它的作用是什么,与 Thread 又有什么区别呢?

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.
 */

从 HandlerThread 源码的注释上可以看出三点:

从以上三点可以总结出其实它和 Thread 基本没什么区别,只是它可以提供一个 Looper 给 Handler。

了解 Handler 机制的话,就知道 Looper 在 Handler 机制中是必不可少的,主线程中的 Looper 对象在 ActivityThread#main 方法中通过 Looper.prepareMainLooper 创建了一个主线程的 Looper 对象,所以在主线程中不需要我们手动来创建 Looper,但是如果是在子线程使用 Handler 的话,那么必须要先创建一个子线程的 Looper 对象才行。如果在子线程中创建 Handler 的话,那么我们应该这样写:

new Thread() {
  @Override
  public void run() {
    super.run();
    Looper.prepare();
    Looper looper = Looper.myLooper();
    Handler handler = new Handler(looper);
    Looper.loop();
  }
}.start();

如果每次都要写这么多的话,那岂不是要疯了,于是 HandlerThread 产生了。

HandlerThread 使用

使用方式和普通的线程没有什么区别,先创建 HandlerThread 对象,然后再调用 start 方法启动。

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        HandlerThread handlerThread = new HandlerThread("HandlerThread");
        handlerThread.start();
        Looper looper = handlerThread.getLooper();

        Handler handler = new Handler(looper, new Handler.Callback() {
            @Override
            public boolean handleMessage(@NonNull Message msg) {
                switch (msg.what) {
                    case 1:
                        Log.e("HandleMessage", "obj = " + msg.obj + " - CurrentThread = " + Thread.currentThread().getName());
                        break;
                }
                return false;
            }
        });


        Message message = Message.obtain();
        message.what = 1;
        message.obj = "HandlerThread测试";
        handler.sendMessage(message);
    }
}

上面代码先创建了一个 HandlerThread 对象,并指定了线程名为 HandlerThrad,并且调用 start 方法启动了线程,调用了 start 方法后,HandlerThread#run 方法才会被执行,Looper 对象才会被创建,所以 handlerThread.getLooper 方法必须要在 handlerThread.start 之后调用,否则 Looper 对象还没有创建。

接着根据获取到的 Looper 对象创建了一个 Handler 对象,然后创建了一个 Message 对象并发送,在 Handler 的回调方法 handleMessage 方法中接收消息。

以为 Looper 是 HandlerThread 线程创建的,所以根据这个 Looper 创建出来的 Handler 是一个异步 Handler,handleMessage 也是在子线程中处理消息。

上面的 Log 打印:

E/HandleMessage: obj = HandlerThread测试 - CurrentThread = HandlerThread

线程为 HandlerThread 线程,并且收到了 Message 里面的字符串消息。

HandlerThread 源码解析

HandlerThread 的源码非常简单,只有几个方法

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;
    }
    
    // 构造函数二,线程名和线程优先级。由Process提供而不是Thread
    public HandlerThread(String name, int priority) {
        super(name);
        mPriority = priority;
    }
    
    // 回调函数
    protected void onLooperPrepared() {
    }

    @Override
    public void run() {
        mTid = Process.myTid(); // 获取当前线程ID
        Looper.prepare(); // 创建Looper和MessageQueue
        synchronized (this) {
            mLooper = Looper.myLooper(); // 获取Looper
            notifyAll();
        }
        Process.setThreadPriority(mPriority); // 设置线程优先级
        onLooperPrepared();
        Looper.loop(); // 开启loop循环
        mTid = -1;
    }
    
    // 加锁获取Looper对象
    public Looper getLooper() {
        if (!isAlive()) {
            return null;
        }
        // 因为是异步线程,所以要加锁等待Looper对象初始化完成
        synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }


        // 获取根据Looper对象创建的Handler,隐藏API
    @NonNull
    public Handler getThreadHandler() {
        if (mHandler == null) {
            mHandler = new Handler(getLooper());
        }
        return mHandler;
    }

    // 马上终止Looper,不管任务有没有执行完成
    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;
    }

        // 返回线程ID
    public int getThreadId() {
        return mTid;
    }
}

就几个方法,注释写的也很清楚了。创建 HandlerThread 有两个构造函数,我们只使用一个参数的构造函数来创建对象就好了,当调用 start 方法的时候,HandlerThread 的 run 方法就会马上被调用,在 run 方法中调用了 Looper.prepare 方法创建一个 Looper 对象,并在创建 Looper 的时候也创建了一个 MessageQueue 对象。

然后使用 synchronized 锁住了代码块,为什么这里要加锁,并且要调用 notifyAll 方法呢?

这还要从另一个方法 getLooper 说起,我们在使用 Looper 创建 Handler 对象时,调用了 getLooper 方法,它是在主线程中执行的,而创建 Looper 对象又是在 run 方法这个子线程中执行的,因为它们处于不同的线程中,这样就无法保证在获取 Looper 对象时一定初始化完成了。所以就需要使用锁来保证获取 Looper 对象的时候一定初始化完成了,如果没有初始化完成,那么就继续等待 Looper 初始化完成再返回。

获取到返回的 Looper 对象赋值给了 mLooper 变量后,调用了回调方法 onLooperPrepared 方法,它是一个空方法,如有需要可以在子类中重写该方法,接着调用了 Looper.loop 方法开启循环。

HandlerThread 内部还提供了一个获取子线程 Handler 的方法 getThreadHandler,可惜它是一个隐藏 API,我们无法调用,只能通过反射的方式去调用了,这样我们就不需要在使用的时候自己创建一个 Handler 对象了。

总结

HandlerThread 本质就是一个线程,使用方式和线程差不多,只不过它提供了一个额外功能就是提供了一个子线程的 Looper,外界可以根据这个 Looper 对象创建一个子线程的 Handler。

上一篇 下一篇

猜你喜欢

热点阅读