HandlerThread的详解

—、Looper , Handler , Message之间的关系
Looper负责的就是创建一个MessageQueue,然后进入一个无限循环体不断从MessageQueue中读取消息,而消息的创建者就是一个或多个Handler 。若消息队列为空,线程则会阻塞等待。
1.Looper


Paste_Image.png

sThreadLocal是一个ThreadLocal对象,可以在一个线程中存储变量。可以看到,在第78行,将一个Looper的实例放入了ThreadLocal,并且75-77行判断了sThreadLocal是否为null,否则抛出异常。这也就说明了Looper.prepare()方法不能被调用两次,同时也保证了一个线程中只有一个Looper实例

Paste_Image.png

在构造方法中,创建了一个MessageQueue(消息队列)。

Paste_Image.png

方法直接返回了sThreadLocal存储的Looper实例,如果me为null则抛出异常,也就是说looper方法必须在prepare方法之后运行。
第114行:拿到该looper实例中的mQueue(消息队列)
121到153行:就进入了我们所说的无限循环。
122行:取出一条消息,如果没有消息则阻塞。
135行:使用调用 msg.target.dispatchMessage(msg);把消息交给msg的target的dispatchMessage方法去处理。Msg的target是什么呢?其实就是handler对象,下面会进行分析。
152行:释放消息占据的资源。
Looper主要作用:
1、 与当前线程绑定,保证一个线程只会有一个Looper实例,同时一个Looper实例也只有一个MessageQueue。
2、 loop()方法,不断从MessageQueue中去取消息,交给消息的target属性的dispatchMessage去处理。
好了,我们的异步消息处理线程已经有了消息队列(MessageQueue),也有了在无限循环体中取出消息的哥们,现在缺的就是发送消息的对象Handler了;

2、Handler
1、首先Looper.prepare()在本线程中保存一个Looper实例,然后该实例中保存一个MessageQueue对象;因为Looper.prepare()在一个线程中只能调用一次,所以MessageQueue在一个线程中只会存在一个。
2、Looper.loop()会让当前线程进入一个无限循环,不端从MessageQueue的实例中读取消息,然后回调msg.target.dispatchMessage(msg)方法。
3、Handler的构造方法,会首先得到当前线程中保存的Looper实例,进而与Looper实例中的MessageQueue想关联。
4、Handler的sendMessage方法,会给msg的target赋值为handler自身,然后加入MessageQueue中。
5、在构造Handler实例时,我们会重写handleMessage方法,也就是msg.target.dispatchMessage(msg)最终调用的方法。
3、HandlerThread
HandlerThread本质上是一个线程类,继承自Thread类,但是HandlerThread有自己的Looper对象,可以进行looper循环,不断从MessageQueue中取消息。
可以看出,我们将前面的创建的HandlerThread实例的Looper对象传递给了Handler,这使得该Handler拥有了HandlerThread的Looper对象,通过该Handler发送的消息,都将被发送到该Looper对象的MessageQueue中,且回调方法的执行也是执行在HandlerThread这个异步线程中。
值得注意的是,如果在handleMessage()执行完成后,如果想要更新UI,可以用UI线程的Handler发消息给UI线程来更新。

源码角度解析HandlerThread
HandlerThread在run()方法中,执行了Looper.prepare()方法,mLooper保存并获得了当前线程的Looper对象,并通过notifyAll()方法去唤醒等待线程,最后执行了Looper.loop()开启looper循环语句。也就是说,run()方法的主要作用就是完成looper机制的创建。

Handler通过getLooper()方法获取looper对象。该方法首先判断当前线程是否启动?如果没有启动,返回null;如果启动,进入同步语句。如果mLooper为null,代表mLooper没有被赋值,则当前调用线程进入等待阶段。直到Looper对象被创建且通过notifyAll()方法唤醒等待线程,最后才会返回Looper对象。我们看到在run()方法中,mLooper在得到Looper对象后,会发送notifyAll()方法来唤醒等待的线程。

Looper对象的创建在子线程的run()方法中执行,但是调用getLooper()的地方是在主线程中运行,我们无法保证在调用getLooper()时Looper已经被成功创建,所以会在getLooper()中存在一个同步的问题,通过等待唤醒机制解决了同步的问题

HandlerThread退出
无论是调用quit()方法还是quitSafely(),最终都将执行到MessageQueue中的quit()方法。
quit()方法最终执行的removeAllMessagesLocked()方法,该方法主要是把MessageQueue消息池中所有的消息全部清空,无论是延迟消息(延迟消息是指通过sendMessageDelayed或通过postDelayed等方法发送)还是非延迟消息。

quitSafely()方法,其最终执行的是MessageQueue中的removeAllFutureMessagesLocked方法,该方法只会清空MessageQueue消息池中所有的延迟消息,并将消息池中所有的非延迟消息派发出去让Handler去处理完成后才停止Looper循环,quitSafely相比于quit方法安全的原因在于清空消息之前会派发所有的非延迟消息。最后需要注意的是Looper的quit方法是基于API 1,而Looper的quitSafely方法则是基于API 18的。

推荐阅读更多精彩内容