1. 介绍
做Android开发一定会用到Handler,通常是用来将一些子线程中得到的结果post到主线线程,即子线程与主线程的切换和通信。一些第三方的库和官方库涉及到线程的基本上也是用的Handler,例如:EventBus(3.3.1),LiveData(2.3.1)。可以说只要有异步线程与主线程通信的操作基本上都是通过Handler实现。
那么,Handler得到如此青睐的背后是无奈的选择还是另有其因,本文带你一探究竟。
2. Handler的使用
使用Handler来发送一个Message或Runnable来处理业务(Runnable会被封装成Message),调用hander.post(...)
方法在某个线程并不意味着处理就在哪个线程,在哪个线程处理的关键在于创建Handler时传入的参数Looper是哪个线程的,若入参Looper是Looper.getMainLooper(),则在主线程。
示例代码:
// 示例类
class Xxx{
// 主线程 Hanlder
fun aMethod(){
// 主线程的handler
val handler = Handler(Looper.getMainLooper())
// 发送消息
handler.sendEmptyMessage(1)
// post Runnable
handler.post {
// do something
// 这里的逻辑会在主线程
}
}
// 子线程 Handler
fun bMethod(){
// 开启一个线程
thread {
// 初始化Looper;若不调用该方法Looper.myLooper()将为空
Looper.prepare()
// 初始化Handler
val handler = Looper.myLooper()?.let { Handler(it) }
handler?.sendEmptyMessage(1)
handler?.post {
// do something... 这里的逻辑会在子线程
// 退出looper,释放线程
Looper.myLooper()?.quit()
}
// looper开始死循环
Looper.loop()
// 退出looper的逻辑写在这里没有效果,上面loop是死循环,不会执行到这里
// Looper.myLooper()?.quit()
}
}
// 常用写法
fun cMethod(){
thread {
val handler = Handler(Looper.getMainLooper())
handler.sendEmptyMessage(1)
handler.post {
// do something
// 这里的逻辑会在主线程
}
}
}
}
分析一下异同点:
-
a 和 b
相同点:基本没有
不同点:
- aMethod 在主线程直接调用;bMethod新开一个线程调用
- aMethod 没用调用Looper额外方法;bMethod调用了
Looper.prepare()
、Looper.loop()
、Looper.myLooper()?.quit()
- aMethod 的Handler的入参是
Looper.getMainLooper()
;bMethod是Looper.myLooper()
- aMethod 的逻辑在主线程;bMethod逻辑在子线程
-
a 和 c
相同点:
- Handler的入参是
Looper.getMainLooper()
- 没用调用Looper额外方法
- 逻辑都是在主线程
不同点:
- aMethod在主线程直接调用;cMethod新开一个线程调用
- Handler的入参是
-
b 和 c
相同点:
- 都是新开一个线程调用
不同点
- bMethod调用了
Looper.prepare()
、Looper.loop()
、Looper.myLooper()?.quit()
;cMethod没用调用Looper额外方法 - bMethod是
Looper.myLooper()
;cMethod的Handler的入参是Looper.getMainLooper()
- bMethod逻辑在子线程;cMethod的逻辑在主线程
提问:
aMethod在主线调用,bMethod新开一个线程,cMethod新开一个线程,为什么只有bMethod的逻辑是在子线程?
bMethod和cMethod都是新开一个线程,为什么bMethod需要调用
Looper.prepare()
等方法而cMethod不需要?
分析:
- 只有bMethod在创建Handler时入参是
Looper.myLooper()
,其余都是Looper.getMainLooper()
;myLooper()
表示当前线程的Looper实例,而getMainLooper()
则是主线程中的Looper实例,故只有bMethod是在子线程中处理逻辑。 - bMethod和cMethod都是新开一个线程,bMethod需要调用
Looper.prepare()
而cMethod不需要,关键还是在于创建Handler时入参不同。如果bMethod不调用Looper.prepare()
则Looper.myLooper()
为空,会报错;而cMethod直接调用Looper.getMainLooper()
不会报错,是因为在主线程中已经创建好了Looper实例。
3. 原理分析
分析基于Android api-31,不同版本源码可能有差异。分析之前思考几个问题:
- Handler是如何切换线程的?
- Looper的作用是什么?一个线程有几个Looper?
- 为什么在Handler初始化时要传递Looper,与Looper是什么关系?
- Handler发送的Message是怎么处理的?
3.1 涉及到的相关类
按照惯例,上类图:
Message:Handler中被传递的数据对象
MessageQueue:负责管理Message的队列,真实的实例是存在Looper中,Handler中的
mQueue
实际上是在初始化的时候指向了Looper中的实例Looper:负责管理MessageQueue,调用looper是开启一个死循环,不停的从MessageQueue中获取Message来处理直到调用了quit()
Handler:主角,内部有一个指向Looper维护的MessageQueue的实例,在sendMessage或者post Runnable时实际上都是向该队列中添加Message
从上面的分析了解到,实际上完整的Handler的使用是:
Looper.prepare()
val handler = Looper.myLooper()?.let { Handler(it) }
handler?.post {
// do something
Looper.myLooper()?.quit()
}
Looper.loop()
接下来就根据使用流程分析一下源码中的实现。
3.2 创建Looper
Looper的实例创建是通过静态方法prepare()
实例化的,构造方法是private
的:
public final class Looper {
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
final MessageQueue mQueue;
final Thread mThread;
// 私有构造方法
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
// 为当前线程初始化 Looper
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
// 如果当前线程有looper,则抛异常
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
// 直接new一个Looper并放到sThreadLocal中
sThreadLocal.set(new Looper(quitAllowed));
}
}
从prepare(boolean quitAllowed)
中可以看出,一个线程只能有1个Looper实例。通过prepare()
方法为当前线程创建来唯一的一个Looper,就可以进行下面的流程了。
3.3 创建Handler
/**
* handler构造方法中 没有 Looper的入参都被标记过时了
* 官方也在提醒开发者显示的指明looper,更容易区分handler在哪个线程使用
*/
public class Handler {
// 通过注解 @NonNull 来限制入参不能为空
public Handler(@NonNull Looper looper) {
this(looper, null, false);
}
// 该构造方法可以传入一个 callback ,在处理消息时,callback的优先级高于子类实现的handleMessage(msg)方法
public Handler(@NonNull Looper looper, @Nullable Callback callback) {
this(looper, callback, false);
}
// 给成员变量赋值
public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
mLooper = looper;
// 这里将成员变量mQueue指向了looper中的mQueue,故在handler的中操作mQueue也就是操作looper中的mQueue
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
// 子类实现处理消息的逻辑,优先级低于 mCallback
public void handleMessage(@NonNull Message msg) {
}
}
在Handler中的构造方法中Looper参数是不能为空的,因此可以反推,在创建Handler实例时必须先调用Looper.prepare()方法为线程初始化Looper实例。
3.4 Handler发送Message
Handler提供了一系列发送消息的方法,例如send()和post()。不管哪种方法最终都会走到Handler.enqueueMessage(MessageQueue, Message, long)
。例如:
Handler.post(Runnable)
-> sendMessageDelayed(Message, long)
-> sendMessageAtTime(Message, long) -> enqueueMessage(MessageQueue, Message, long) > return queue.enqueueMessage(msg, uptimeMillis);
在enqueueMessage(MessageQueue, Message, long)
queue中,通过前面介绍也就是Looper的mQueue
中。
至此,Handler的使命完成了一半,即将待处理的Message添加到Looper中的mQueue
中。
Tips:Handler在发送消息时,代码一定是写到Looper.loop()之前,loop()方法是死循环,后面的代码都不会执行。
看一下Message相关源码:
public final class Message implements Parcelable {
// 当前 message 对应的目标处理者,即处理当前message的 handler
Handler target;
// Message处理消息的回调,优先级最高
Runnable callback;
}
Message有一个Handler的成员变量,表示可以处理当前message
的handler
实例,上面分析到handler
最后都会调用enqueueMessage(...)
方法:
public class Handler {
// 向队列中添加 Message
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
// message 的 taget 赋值
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
// 将消息添加到队列
return queue.enqueueMessage(msg, uptimeMillis);
}
}
在enqueueMessage(...)
中可以看到被发送的消息的target
实例被赋值为this
,即谁发送的消息谁处理。
3.5 Message的分发与处理
Looper创建好了,Message也被添加到MessageQueue中,接下来看一下Message是怎么处理的。
上面分析到Looper最后需要调用Looper.loop()
:
public final class Looper {
/**
* 在当前线程启动消息对象。调用 quit() 退出循环
*/
@SuppressWarnings("AndroidFrameworkBinderIdentity")
public static void loop() {
// 当前的looper
final Looper me = myLooper();
// 为空抛异常
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
// other code ...
// 死循环开始
for (;;) {
// 如果为 false 则退出循环
if (!loopOnce(me, ident, thresholdOverride)) {
return;
}
}
}
// 处理一次消息
private static boolean loopOnce(final Looper me, final long ident, final int thresholdOverride) {
// 从消息对列中取消息
Message msg = me.mQueue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return false;
}
// other code ...
try {
// 调用 msg 持有的 handler 的 dispatchMessage 方法
msg.target.dispatchMessage(msg);
// other code ...
} catch (Exception exception) {
// other code ...
throw exception;
} finally {
// other code ...
}
// other code ...
// 回收消息
msg.recycleUnchecked();
return true;
}
// 退出循环
public void quit() {
mQueue.quit(false);
}
public void quitSafely() {
mQueue.quit(true);
}
}
loop()
方法开启了循环,真正分发消息是在loopOnce()
方法,在经过一系列判断后会调用msg.target.dispatchMessage(msg)
,该方法即是回调到了Handler的dispatchMessage(msg)
,消息交给Handler处理。那么msg
是从哪里来的呢?第一行代码me.mQueue.next()
,从消息的队列里取到下一个消息:
public final class MessageQueue {
// 标记队列是否退出
private boolean mQuitting;
// 从对列中取下一个待处理的消息
Message next() {
// other code ...
// 开启死循环
for (;;) {
// other code ...
synchronized (this) {
// other code ...
if(...){
// 返回待处理消息
return msg;
}
// other code ...
// 如果是推出来,则返回为空
if (mQuitting) {
dispose();
return null;
}
// other code ...
}
// other code ...
}
}
// 退出
void quit(boolean safe) {
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
synchronized (this) {
if (mQuitting) {
return;
}
// 改变退出状态
mQuitting = true;
if (safe) {
removeAllFutureMessagesLocked();
} else {
removeAllMessagesLocked();
}
// We can assume mPtr != 0 because mQuitting was previously false.
nativeWake(mPtr);
}
}
}
可以看到,在取下一个消息时也是死循环,且只有一种情况会返回为空,即mQuitting == true
时,当前队列需要推出时返回为空。结合外层的判断理解,当MeeageQueue.next()
为空时Looper.loopOnce(...)
为flase
,Looper.loop()
也会退出循环。为什么会有2个死循环呢,一个行不行?
分析一下每个死循环的作用,首先MeeageQueue.next()
的死循环是一直需要从MessageQueue中取消息,如果没有就一直循环等到有消息了就返回,队列中是不确定有多少个消息的,即是当前没有也会有再添加的,所以除非主动退出,否则就会一直循环下去找到消息为止。而Looper.loop()
的死循环是处理完一个消息,接着处理下一个,同样Looper也是不确定是否还有下一个待处理的消息,也就会一直循环下去。
MeeageQueue.next()
的死循环作用是查找到消息为止;Looper.loop()
的死循环作用是不停的处理消息。
那么Looper.loop()
什么时候退出呢?首先主线程的Looper是不能退出的,退出就会抛异常【见3.6】,其他线程Looper可以调用Looper.quit()
或Looper.quitSafely()
来退出循环。其原理就是改变队列里的mQuitting
的值。
通过调用msg.target.dispatchMessage(msg)
已经把消息交给了Handler处理:
public class Handler {
// handler处理消息的回调
public interface Callback {
boolean handleMessage(@NonNull Message msg);
}
// 子类实现该方法处理消息
public void handleMessage(@NonNull Message msg) {
}
/**
* 在这里处理消息
*/
public void dispatchMessage(@NonNull Message msg) {
// 回调message的runnable
if (msg.callback != null) {
handleCallback(msg);
} else {
// handler成员变量 Callback mCallback 处理消息;mCallback是在handler构造函数中赋值的
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
// handler处理消息的方法
handleMessage(msg);
}
}
// 通过message的callback处理消息
private static void handleCallback(Message message) {
message.callback.run();
}
}
可以看到有3个都处理了消息,但是消息只会被处理1次,其优先级为:消息自带Runnable处理 > handler成员变量Callback 处理 > handler方法处理。
3.6 小结
- MainLooper初始化是在ActivityThread中
public final class ActivityThread {
public static void main(String[] args) {
// other code ...
// 初始化 mainLooper,内部会调用 prepare(boolean) 方法,并给成员变量 sMainLooper 赋值
Looper.prepareMainLooper();
// other code ...
// 开始死循环
Looper.loop();
// loop退出则抛异常
throw new RuntimeException("Main thread loop unexpectedly exited");
}
}
看一下一开始的问题:
-
Handler是如何切换线程的?
通过Looper切换线程,初始化handler是传递的Looper决定来处理消息时的线程。
-
Looper的作用是什么?一个线程有几个Looper?
Looper通过循环从MessageQueue中取出Message交给handler处理。1个线程有且只有1个Looper
-
为什么在Handler初始化时要传递Looper,与Looper是什么关系?
Handler内部的MessageQueue需要指向Looper中的MessageQueue,这样就可以间接向Looper中的MessageQueue中添加消息,而Looper在哪个线程取出消息和处理消息的操作就在哪个线程,故Handler是需要通过Looper实现切换线程的操作。因为Handler持有Looper的成员变量,故与Looper是关联关系,而且Handler无法脱离Looper单独工作,所以也可以说是组合关系(特殊的关联关系的一种)。
4. 常见问题
- Activity中使用匿名内部类或非静态内部类,会造成内存泄漏吗?如果会,为什么?
答:会造成内存泄漏,匿名内部类和非静态内部类会持有外部类的引用即Activity,当Activity销毁触发垃圾回收(GC)时,若此时Handler正在处理消息,则不会被GC机制回收,故Activity会发生泄漏。
解决:正常使用外部类继承Handler、静态内部类(还可使用WeakReference弱引用持有外部类)、外部类生命周期结束时清空消息队列(removeCallbacksAndMessages(null)
)
5. 总结
- Handler负责添加和处理消息;Message负责携带信息是被处理的;MessagQueue负责管理消息;Looper负责分发消息;
- 主线程Looper不能退出,退出则报异常;
- 子线程Looper需要自己调用
Looper.myLooper().quit()
方法退出; - 子线程在创建Handler传入
Looper.myLooper()
时需要先创建Looper; - 主线的Looper是在启动时
ActivityThread
中创建的; -
Handler.handleMessage()
所在的线程是 Looper.loop() 方法被调用的线程,并不是创建 Handler 的线程;
参考: