这篇文章应该是除夕之前的最后一篇文章,写文章的一个很深的体会,就是一个知识点虽然自己能理解,可以说出来,但是在写的时候要花很多时间,因为要让读者可以很好理解,不然写文章就没有意义了。
进入正题,Android消息机制基本是面试必问的知识点,今天结合源码和面试中常问的点,进行一个分析和总结。
开始源码分析
Handler的使用
我们一般使用Handler是这样
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
if (msg.what == 1){
//
}
}
};
Message message = Message.obtain();
message.what = 1;
//1
handler.sendMessage(message);
//或者2
handler.sendMessageDelayed(message,1000);
//或者3
handler.post(new Runnable() {
@Override
public void run() {
}
});
接下来开始分析,首先看下 Handler构造方法
Handler 构造方法
public Handler() {
this(null, false);
}
public Handler(Callback callback, boolean async) {
...
mLooper = Looper.myLooper();
//1
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
注释1:主线程默认有一个Looper,而子线程需要手动调用 Looper.prepare() 去创建一个Looper,不然会报错。
看下主线程Looper在哪初始化的,应用的入口是 ActivityThread 的main方法
ActivityThread#main
public static void main(String[] args) {
...
//1
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
Trace.traceEnd(64L);
//2
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
注释1:Looper.prepareMainLooper(),
注释2:Looper开启消息循环
接下来分析下
Looper.prepareMainLooper()
public static void prepareMainLooper() {
//1
prepare(false);
Class var0 = Looper.class;
synchronized(Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
} else {
sMainLooper = myLooper();
}
}
}
可以看到调用prepare(false),参数false表示不允许退出。然后是给sMainLooper赋值,看下prepare方法
Looper.prepare()
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal();
...
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");
} else {
//1
sThreadLocal.set(new Looper(quitAllowed));
}
}
可以看到 prepare 是创建一个Looper,并放到 ThreadLocal里。
准备工作做好了,看下Looper.loop()
Looper.loop()
public static void loop() {
//1 从ThreadLocal 获取Looper
Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
} else {
//2 Looper里面有一个消息队列
MessageQueue queue = me.mQueue;
Binder.clearCallingIdentity();
long ident = Binder.clearCallingIdentity();
while(true) {
//3 死循环获取消息
Message msg = queue.next();
if (msg == null) {
return;
}
...
long end;
try {
// 4,获取到消息,处理
msg.target.dispatchMessage(msg);
end = slowDispatchThresholdMs == 0L ? 0L : SystemClock.uptimeMillis();
} finally {
if (traceTag != 0L) {
Trace.traceEnd(traceTag);
}
}
...
// 5.消息回收,可复用
msg.recycleUnchecked();
}
}
}
public static Looper myLooper() {
return (Looper)sThreadLocal.get();
}
注释1: 从ThreadLocal 获取Looper,子线程需要调用Looper.prepare,不然会报错,上面说过。
注释2:获取消息队列
注释3:queue.next(),获取一个消息
注释4:处理消息
接下来分成几个小点分析一下
1.mQueue 是什么
private Looper(boolean quitAllowed) {
this.mQueue = new MessageQueue(quitAllowed);
this.mThread = Thread.currentThread();
}
mQueue是一个消息队列,在创建 Looper 的时候创建的。
2.MessageQueue#next
Message next() {
...
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
// 1 native方法,应该是唤醒线程的
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
//2.取出下一条消息
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
// 3.延时的处理,计算定时唤醒时间
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
//4.获取到一条消息,因为是消息队列是链表结构,所以需要调整一下链表
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages have been handled.
...
//5.这里提到 IdleHandler,是个什么东西?下面会分析
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler
boolean keep = false;
try {
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
}
}
next方法,取出一条消息,如果有设置延时,计算一下多久后执行,然后在下一次循环,注释1处,调用native方法进行一个类似闹钟的设置,时间到的话会唤醒next方法。消息队列是单链表的数据结构,所以取出一条消息之后链表需要移动,注释4处。
消息取出来之后就要处理了,即
msg.target.dispatchMessage(msg);
msg.target是一个Handler
注释5是扩展分析,面试映客直播时候被问到Handler中的Idle是什么,这里就写一下
在获取不到message的时候才会走注释5的代码,也就可以理解为 IdleHandler是消息队列空闲(或者主线程空闲)时候才会执行的Handler。
IdleHandler 是一个接口,只有一个方法 queueIdle() ,调用addIdleHandler(IdleHandler handler) 这个方法的时候会将handler添加到list中
public void addIdleHandler(@NonNull IdleHandler handler) {
synchronized (this) {
mIdleHandlers.add(handler);
}
}
然后在消息队列空闲的时候会遍历这个list,执行里面IdleHandler的queueIdle方法。
有什么应用场景呢?
leakcanary 中使用到这个
// 类:AndroidWatchExecutor
private void waitForIdle(final Retryable retryable, final int failedAttempts) {
// This needs to be called from the main thread.
Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
@Override public boolean queueIdle() {
postToBackgroundWithDelay(retryable, failedAttempts);
return false;
}
});
}
leakcanary中检测内存泄漏的耗时任务会等到主线程空闲才执行
3.Handler#dispatchMessage
public void dispatchMessage(Message msg) {
// 1
if (msg.callback != null) {
handleCallback(msg);
} else {
// 2
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
// 3
handleMessage(msg);
}
}
public void handleMessage(Message msg) {
}
一般走的是注释3,也就是我们重写的 handleMessage方法,
注释1 :调用 Message.obtain(Handler h, Runnable callback)
传的callback。
注释2:创建Handler的时候使用这个构造Handler(Callback callback)
Looper开启循环,从MessageQueue取消息并处理的流程分析完了,还差一个消息的入队
Handler#sendMessage
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + 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);
}
sendMessage 调用了sendMessageDelayed,最终调用了enqueueMessage,进行入队
Handler#enqueueMessage
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
最终调用了 MessageQueue 的 enqueueMessage
MessageQueue#enqueueMessage
boolean enqueueMessage(Message msg, long when) {
...
synchronized (this) {
...
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
// 1.满足3个条件则插到链表头部
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// 2.否则插到链表中间,需要移动链表
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
// 链表移动,延时的消息会插入到合适的位置
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
//如果需要,调用native方法唤醒
nativeWake(mPtr);
}
}
return true;
}
注释1:p是队列头部,满足3个条件则把消息放到队列头部
1.队列中没有消息,p==null
2.入队的消息没有延时
3.入队的消息的延时比队列头部的消息延时短
注释2:消息插入到链表中,需要移动链表,对比消息的延时,插入到合适的位置
好了,消息机制的分析大概就是这些了,接下来结合面试中的问题再回顾一下
面试中相关的问题
1.说一下Handler机制
1.在应用启动的时候,也就是ActivityThread的main方法里面,创建了Looper和MessageQueue,然后调用Looper.loop 开启消息循环
2.消息循环是这样的,调用MessageQueue的next方法,循环从消息队列中取出一条消息,然后交给Handler去处理,一般是回调handleMessage方法,取不到消息就阻塞,直到下一个消息入队或者其它延时消息到时间了就唤醒消息队列。
3.消息入队,通过调用handler的sendMessage方法,内部是调用MessageQueue的enqueueMessage方法,进行消息入队,入队的规制是:队列没有消息,或者要入队的消息没有设置delay,或者delay时间比队列头的消息delay时间短,则将要入队的消息放到队列头,否则就插到队列中间,需要移动链表。
2.发送延时消息是怎么处理的
这个通过上面的分析应该很容易答出来了
根据消息队列入队规制,如果队列中没消息,那么不管要入队的消息有没有延时,都放到队列头。如果队列不空,那么要跟队列头的消息比较一下延时,如果要入队的消息延时短,则放队列头,否则,放到队列中去,需要移动链表。
入队的规制的好处是,延时越长的消息在队列越后面,所以next方法取到一个延时消息时,如果判断时间没有到,就进行阻塞,不用管后面的消息,因为队列后面的消息延迟时间更长。
ok,关于Handler消息机制的分析到此结束,有问题欢迎留言交流。