一:背景
最近行情不好,不幸被裁,老板耍赖不肯给全额赔偿,没办法只能准备面试,但是上家一天996,基本上没有学习到什么新的知识点,老的知识点又忘的差不多了。所以准备写一下对应知识点的文章,方便自己检查自己的掌握情况,也有利于自己记住这些知识点。
二:正文
handler已经是一个老生常谈的东西了,基本上大家都对它了解的比较多,本文尽量深入挖掘它的底层和不为人知的部分,错误之处还请大家指出
1.概述
上图是整个handler机制的运行流程图,简单概括各个类的作用就是:
- handler:发送和处理消息
- Message:消息对象,承载消息内容
- MessageQueue:消息队列
- Looper:消息轮询器,不断查询消息,取出消息
2.功能实现
Handler
构造方法:
public Handler(@Nullable Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
mLooper = Looper.myLooper();
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;
}
handler的构造方法最终调用该方法,通过该方法可以看到,创建对象的时候获取到了looper对象,获取到了messagequeue。 点击查看Looper.myLooper()会发现是获取当前线程的ThreadLocal中的looper对象,所以在子线程中使用handler需要先Looper.prepare(),通过该方法创建当前线程的looper对象。然后调用Looper.loop使用过
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));
}
让我们看看sThreadLocal.set(new Looper(quitAllowed));
public void set(T value) {
//获取当前线程id
Thread t = Thread.currentThread();
//通过线程id获取它内部的map(threadLocals)(Thread类中维护了线程唯一的ThreadLocalMap)
ThreadLocalMap map = getMap(t);
//如果线程内部的map已经存在则直接调用去set,如果没有则创建一个。
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
可以看到把创建的looper对象设置给了当前线程 PS:主线程中之所以不需要调用 Looper.prepare() 是因为ActivityThread中的main方法中调用了Looper.prepareMainLooper(); 系统帮我们初始化了looper对象
发送消息:
通过调用handler的各种send或者post消息方法最终调用到了enqueueMessage,把消息放入looper中的messagequeue中,按执行时间排序,其中有异步消息也有同步消息,同步消息按时间排序,会受同步屏障影响,异步消息不受同步屏障影响,
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
处理消息
根据发送消息的代码可以得知,消息中的target对象是当前的handler对象,查阅Looper的loop方法可以看到
msg.target.dispatchMessage(msg);
可知是调用了handler的dispatchMessage方法。
/**
* Handle system messages here.
*/
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
/**
* Subclasses must implement this to receive messages.
*/
public void handleMessage(@NonNull Message msg) {
}
根据代码可以看出如果有msg的callback是走handler的handleMessage方法,没有msg的callback再走handler的构造方法的callback。
Looper
looper主要讲一下它的loop方法,是怎么轮询的,为什么死循环不会导致ANR。
for (;;) {
Message msg = queue.next(); // might block 阻塞处
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// This must be in a local variable, in case a UI event sets the logger
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
// Make sure the observer won't change while processing a transaction.
final Observer observer = sObserver;
Object token = null;
if (observer != null) {
token = observer.messageDispatchStarting();
}
long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);
try {
msg.target.dispatchMessage(msg);//分发消息
if (observer != null) {
observer.messageDispatched(token, msg);
}
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} catch (Exception exception) {
if (observer != null) {
observer.dispatchingThrewException(token, msg, exception);
}
throw exception;
} finally {
ThreadLocalWorkSource.restore(origWorkSource);
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
msg.recycleUnchecked();
}
以上就是loop方法中最关键的地方,可以看到死循环处理消息,同时queue.next()进行了阻塞。那么为什么没有导致ANR呢?
首先什么是ANR呢?ANR顾名思义就是应用程序无响应,纸面意思就是用户操作了,没有效应。ANR是Android系统主动抛出来的一个异常,不同的地方时间不同,输入事件后主线程5秒没响应就会抛出ANR。 这是因为这里用到了Linux系统的一个pipe和epoll机制。
- pipe机制,在没有消息时阻塞线程并进入休眠释放cpu资源,有消息时唤醒线程
- epoll机制,读端只会在写端写入数据时触发
比方说用户点击,此时就会产生一个点击事件被系统的屏幕管理服务捕获,再通过Binder传递给ApplicationThread,然后applicationthread就会通过handler发送消息给activitythread,此时messagequeue中就有消息,就不会堵塞在此会进行对应的逻辑处理
可以看到Loop方法中有这样一段代码
try {
msg.target.dispatchMessage(msg);//分发消息
if (observer != null) {
observer.messageDispatched(token, msg);
}
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} catch (Exception exception) {
if (observer != null) {
observer.dispatchingThrewException(token, msg, exception);
}
throw exception;
}
Observer是Looper中的接口,里面定义了一些方法,可以用来监听当前handler到底处理了那些message,用来解决一些疑难问题很有用处。
Message
消息分为:
- 同步消息:按执行时间在messagequeue中排序,target为当前handler,
- 异步消息:跟同步消息结构都一样,只是flags = FLAG_ASYNCHRONOUS;
- 屏障消息:用在需要优先级高的场景,target为null,同时把自己放在队头,当target为null时,messagequeue会把异步消息放在前面执行
MessageQueue
获取下一个消息逻辑
Message next() {
// Return here if the message loop has already quit and been disposed.
// This can happen if the application tries to restart a looper after quit
// which is not supported.
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
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;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous()); // 寻找异步消息
}
if (msg != null) {
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.
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;
}
。。。。
}
}
同步屏障
场景用处:view绘制时使用,在ViewRootImpl中的scheduleTraversals发送同步屏障消息,确保view绘制消息能够及时执行,界面及时绘制,不会导致卡顿,同步屏障设置后需要调用解除同步屏障方法removeSyncBarrier
@UnsupportedAppUsage
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
//未设置 target
private int postSyncBarrier(long when) {
// Enqueue a new sync barrier token.
// We don't need to wake the queue because the purpose of a barrier is to stall it.
synchronized (this) {
final int token = mNextBarrierToken++;
final Message msg = Message.obtain();
msg.markInUse();
msg.when = when;
msg.arg1 = token;
Message prev = null;
Message p = mMessages;
if (when != 0) {
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
}
if (prev != null) { // invariant: p == prev.next
msg.next = p;
prev.next = msg;
} else {
msg.next = p;
mMessages = msg;
}
return token;
}
}