概述
Handler、Looper、MessageQueue
这几个,相信接触android开发
的,基本都能说出来这三者的基本关系,但是很多人可能也是停留在三者的调用关系上,你如果问他postDelay是如何实现的
,为啥子线程中要先prepare
,为啥handler容易产生内存泄露
等等,他可能会一知半解,笔者还是决定从源码的角度重新审视这些问题
从问题中看源码
如果让你在线程中实现handler
,你可能会很快的写下如下代码,那笔者就还是以这个为切入点,分析前面提出的几个问题
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
Handler handler = new Handler(Looper.myLooper());
Looper.loop();
}
}).start();
为啥子线程需要prepare
,如果不调用会有什么问题?
我们直接进入Looper.prepare()
的源码中看看:
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
我们可以看到调用的其实是一个静态方法
,内部的处理就是实例化了一个Looper对象
,存放在sThreadLocal
中了,ThreadLocal
你可以理解为是线程本地存储,用于保存线程共享变量,如果不调用prepare
直接进行loop操作
,会怎样呢?
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
public static void loop() {
final Looper me = myLooper();
if (me == null) {
//如果没有进行prepare操作会抛异常
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
//拿到当前线程存储的MessageQueue
final MessageQueue queue = me.mQueue;
......
for (;;) {
//如果poseDelay方法有延时参数,这个方法会阻塞,等待延时
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
......
final long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
......
final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
final long end;
try {
//调用handler的dispatchMessage方法
msg.target.dispatchMessage(msg);
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
......
msg.recycleUnchecked();
}
}
我们可以很清楚的看到第一行调用的实际是myLooper方法
,从线程本地存储空间中拿到共享变量Looper的实例
,因此如果子线程中没有进行prepare操作
,这里就会抛出异常throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
那么looper和handler究竟什么联系呢?
下面是Handler的构造方法
:
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
可以看到handler
在初始化的时候,会持有looper以及对应looper的MessageQueue
的实例
下面是Message类的属性
:
public final class Message implements Parcelable {
public static final Creator<Message> CREATOR = null;
public int arg1;
public int arg2;
public Object obj;
public Messenger replyTo;
public int sendingUid;
public int what;
......
long when;
Handler target;
Runnable callback;
Message next;
......
}
可以很清楚的看到Message实例
本身会持有handler引用
,也会有延迟时长属性when
,以及下一个Message
的字段next
相信看了上述Looper,Handler,MessageQueue
,你对他们的持有关系
应该有了解了
handler的postDelayed是如何实现的?
public final boolean postDelayed(Runnable r, long delayMillis)
{
return sendMessageDelayed(getPostMessage(r), delayMillis);
}
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
通过上面的源码,我们可以了解一点:
postDelay
最终是调用MessageQueue的enqueueMessage方法,根据延迟时长
完成消息排队
而Message
你可以理解为是一个节点,里面有next字段标识下一个消息
,这里要注意上面的loop()方法中有一行代码
Message msg = queue.next(); // might block
因此当loop方法
循环从MessageQueue中
读取消息,这个MessageQueue
的next
方法会根据之前定义的延迟时长
进行等待,直到达到这个时长才会返回,然后调用message.target
即我们目标的handler
中的dispatchMessage方法
handler为什么容易造成内存泄露
我们在使用handler
的时候,如果不注意:很容易会写出
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
要知道,匿名内部类会持有外部的引用
,比如是一个activity引用
,那么你如果了解上述的延迟消息的实现原理就会明白
,当我们发送延迟消息
的时候,这个消息会被存放在MessageQueue中
,等待next方法返回
,而对应的Message
又会持有handler对象
,就会导致我们最终引用的这个activity一直无法释放
,从而导致内存泄露的产生
为啥主线程中调用loop不会阻塞呢?
每个应用都有一个主线程,当主线程启动的时候其实也会调用loop
方法,但这个过程会不断的循环从MessageQueue
中取消息来执行,因为android的设计
本身是基于事件驱动的
,因此我们都主线程中的操作都可以看做是不同的事件
,最终交由主线程的handler
来执行,因此如果我们在主线程中做过多耗时操作
,会导致主线程阻塞,即ANR的产生,这个消息循环的模型跟以前在Windows
中Win32下的消息循环类似