Handler机制笔记
该文章是一篇通过阅读Handler源码解析Handler消息机制的学习笔记。重点分析了消息循环、消息分发和消息发送的原理
创建
Handler消息机制的Java层核心服务的创建入口位于SystemServer.java中。其通过main函数中的run()方法中实现创建;而在应用中,当应用程序启动后会进入ActivityThread.java中的main函数。其主线程Handler在这里进行初始化。双方核心代码均如下所示
Looper.prepareMainLooper(); //初始化
Looper.loop(); //开始循环
在主线程Looper初始化后,loop方法中无限循环读取新消息,并分发给相应处理者
Looper初始化
Handler消息机制通过ThreadLocal实现线程区分。其核心代码位于Looper.prepare(boolean quitAllowed)中。其中quitAllowed若为true,则代表该Handler为主线程handler。
主线程Looper准备方法
/**
* 官方不推荐直接调用该方法。其应当有Android环境进行初始化
*/
public static void prepareMainLooper() {
prepare(false);
synchronized(Looper.class) {
if (sMainLooper != null) { //判断主线程是否已存在运转的Looper。若存在则抛错
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper
}
}
该方法直接调用Looper的静态方法prepare(),以当前线程为主线程。并同步判断是否重复调用。若非重调用则将该looper记录在静态变量中作为主Looper
Looper实际准备方法
...
static final threadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
...
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) { //判断Looper是否已存在于内存中,若是则抛错
throw new RuntimeException("Only one Looper may be create per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
在Looper的prepare(boolean quitAllowed)方法中,首先通过静态变量sThreadLocal尝试获取当前Thread中的Looper,以防止重复创建。然后通过构造方法创建Looper并将其存入sThreadLocal中。
Looper构造方法
...
final MessageQueue mQueue;
final Thread mThread;
...
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
在构造方法中对消息队列和其相应的thread变量进行赋值
MessageQueue初始化
...
private long mPtr; // used by native code
...
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}
MessageQueue的初始化记录了允许退出的标记,同时调用native方法进行进一步初始化。至此Looper初始化完成
Looper.loop()
...
private boolean mInLoop; // 判断是否在无限循环的标志位
// If set, the looper will show a warning log if a message delivery (actual delivery time - post time) takes longer than this.
private long mSlowDeliveryThresholdMs;
// True if a message delivery takes longer than mSlowDeliveryThresholdMs.
private boolean mSlowDeliveryDetected;
...
public static void loop() {
final Looper me = myLooper(); // 就是sThreadLocal.get()
if (me == null) { // 判空处理
throw new RuntimeException("no Looper; Looper.prepare() wasn't called on this thread.");
}
if (me.mInLoop) { // 判断是否已经在loop,若已在loop则发出警告log
Slog.w(TAG, "Loop again would have the queued messages be excuted before this one completed.");
}
me.mInLoop = true; // 标志位竖起
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
// ↑获取token定位线程
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
// Allow overiding a threshold with a system prop. e.g.
// adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'
// ↑允许覆盖策略
final int thresholdOverride =
SystemProperties.getInt("log.looger."
+ Process.myUid() + "."
+ Thread.currentThread().getName()
+ ".slow", 0);
me.mSlowDeliveryDetected = false; // 日志相关,于机制不重要
for (;;) {
// 死循环
if (!loopOnce(me, ident, thresholdOverride)) {
return;
}
}
}
loop()方法中主要进行 一些日志和判断工作。最后进入死循环。循环体在方法loopOnce中
Looper.loopOnce()
...
private static Observer sObserver; // 观察者,通过setObserver方法赋值
...
// Poll and deliver single message, return true if the outer loop should continue.
private static boolean loopOnce(final looper me, final long ident, final int thresholdOverride) {
Message = me.mQueue.next(); // might block // 从队列中获取一个消息
if (msg == null) {
// No message indicates that the message queue is quitting.
// 队列空了,说明队列不再等待新消息。说明队列已回收
return false;
}
// 日志相关
...
...
// 核心代码
try {
msg.target.dispatchMessage(msg); // 发送并处理消息
} catch (Exception exception) {
throw exception;
} finally {
// 日志
...
}
// 日志相关
...
...
msg.recycleUnchecked(); // 回收该消息
return true;
}
该方法核心内容为
Message = me.mQueue.next();
msg.target.dispatchMessage(msg);
两句,通过next()方法从消息队列中获取消息,再通过消息的target处理该消息。若流程没有问题则返回true进行下一轮循环。或者block在next()处。
消息队列循环
Handler消息机制的消息存放于MessageQueue中。队列为单链表结构。其维护也是Handler机制的核心
其核心方法为next()
Message next() {
// 聚焦核心功能代码,去除日志等内容
...
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPolltimeoutMillis); // 一个native方法。若当前消息队列中有消息则获得,否则等待
synchronized(this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis(); // 获取系统时钟时间
Message prevMsg = null; // 上一消息
Message msg = mMessage; // 当前消息
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 read. 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 {
mMessage = msg.next; // 锁定信息
}
msg.next = null;
msg.markInUse();
return msg;
}
} else {
// No more message.
nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null
}
...
}
}
}
next()方法通过for循环不断获取msg,其中nativePollOnce()是一个native方法,它会尝试获取消息队列中是否有消息,若有则获得,否则等待。获取到msg后,首先判断其是否存在target,若其为空,说明该msg不包含任何操作,没有处理对象,是一个同步屏障消息。它会尝试遍历消息链表获取下一有效异步msg。
之后判断msg的执行时间(when),若该时间小于循环体最初获取的当前时间(now),则等待至该执行时间。若执行时间大于等于当前时间,则将该msg返回到Looper的loop()方法中,并整理链表。接下来会通过dispatchMessage()方法将消息分发至实际的handler中。
消息入队(发送)
在handler的使用中,我们通过handler.sendMessage(msg)方法发送消息。
// android.os.Handler
final MessageQueue mQueue; // 消息队列实例
public final boolean sendMessage(Message msg) {
return sendMessageDelayed(msg, 0);
}
public final boolean sendmessageDelayed(Message msg, long delayMills) {
if (delayMills < 0) {
delayMills = 0; // 处理下边界
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMills);
}
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()方法会进入enqueueMessage()中
// android.os.Handler
private boolean enqueuemessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimemillis);
}
该方法首先将自己赋值到msg的target属性上,接下来调用了MessageQueue的enqueueMessage()方法。
// android.os.MessageQueue
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
// 判断target是否为空,若为空则抛错
throw new IllegalArgumentException("Message must have a target.");
}
synchronized(this) {
... // 省略一些判断
msg.when = when;
Message p = mMessages; // 链表指针指向的msg,当前指向消息队列队首
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// 消息队列为空、when为0(立刻执行)、when小于队首when(插入队首)
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// 其它情况,需要根据when选择msg插入位置
needWake = mBlock && p.target == null && msg.isAsynchronous();
message prev; // 链表前序变量
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
// 已至队尾或执行时间小于指针msg的执行时间,则插入至指针之前
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
// 执行链表插入
msg.next = p;
prev.next = msg;
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
}
}
如上述代码所示,核心逻辑就是通过待插入msg的when,遍历链表寻找合适的位置插入。之后msg便等待Looper.loop()方法的循环在合适的时机被取出执行了。至此Handler消息循环机制完成
异步消息和同步屏障
在MessageQueue的next()方法中的分析中有出现过对isAsynchronous标记的判断。根据源码可知,当方法发现一条没有target的消息时,会去寻找下一个isAsynchronous为true的消息,从而跳过其他同步消息。以此实现优先处理异步消息的功能。这就是同步屏障机制
同步屏障相关的主要方法都被@UnsupportedAppUsage注解标记,说明其并非开放给开发者使用的共功能。其主要用于View绘制和vsync流程中提高效率使用的。具体是在ViewRootImpl中的相关方法中使用
// android.view.ViewRootImpl
final Choreographer mChoreographer;
final ViewRootHandler mHandler = new ViewRootHandler();
...
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true; // 标记屏障中
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier(); // 开启屏障并记录屏障ID
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
void unscheduleTraversals() {
if (mTraversalScheduled) {
mTraversalScheduled = false; // 标记屏障结束
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier); // 移除屏障
mChoreographer.removeCallbacks(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
}
}
其中在启动屏障时调用了mHandler.getLooper().getQueue().postSyncBarrier(),而在关闭屏障时使用了mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
mChoreographer是一个Choreographer实例,该类主要用于处理绘制、动画等工作。其postCallback()方法和removeCallbacks最终分别走到postCallbackDelayedInternal()方法中
// android.view.Choreographer
private void postCallbackDelayedInternal(int callbackType,
Object action, Object token, long delayMillis) {
if (DEBUG_FRAMES) {
Log.d(TAG, "PostCallback: type=" + callbackType
+ ", action=" + action + ", token=" + token
+ ", delayMillis=" + delayMillis);
}
synchronized (mLock) {
final long now = SystemClock.uptimeMillis();
final long dueTime = now + delayMillis;
mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
if (dueTime <= now) {
scheduleFrameLocked(now);
} else {
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
msg.arg1 = callbackType;
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, dueTime);
}
}
}
private void removeCallbacksInternal(int callbackType, Object action, Object token) {
if (DEBUG_FRAMES) {
Log.d(TAG, "RemoveCallbacks: type=" + callbackType
+ ", action=" + action + ", token=" + token);
}
synchronized (mLock) {
mCallbackQueues[callbackType].removeCallbacksLocked(action, token);
if (action != null && token == null) {
mHandler.removeMessages(MSG_DO_SCHEDULE_CALLBACK, action);
}
}
}
可以看到其发送异步消息的方式和普通消息没有太大区别,只是调用消息的setAsynchronous()方法标记为异步。接下来具体分析各个方法
postSyncBarrier() 开启同步屏障
// android.os.MessageQueue
...
Message mMessages;
// The next barrier token.
// Barriers are indicated by messages with a null target whose arg1 field carries the token.
@UnsupportedAppUsage
private int mNextBarrierToken;
...
@UnsupportedAppUsage
@TestApi
public int postSyncBarrier() {
return postSyncBarrier(SystemClock.uptimeMillis());
}
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;
}
}
// android.os.MessageQueue
@UnsupportedAppUsage
Message next() {
...
...
for (;;) {
...
...
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());
}
...
...
}
...
...
}
}
根据源码可以看出,该段代码看起来很像enqueueMessage()的核心逻辑。它首先将mNextBarrierToken自增并设置为空消息的arg1,并将消息标记为在使用。然后直接将其按照when属性插入到消息链表中。注意,此时的消息target属性未被复制,其依旧为空。回过头看next()核心代码中的第一段判断,若消息的target为空,则循环获取下一异步消息,跳过了中间所有的同步消息。至此,同步屏障的功能实现。
removeSyncBarrier() 移除屏障
// android.os.MessageQueue
@UnsupportedAppUsage
@TestApi
public void removeSyncBarrier(int token) {
// Remove a sync barrier token from the queue.
// If the queue is no longer stalled by a barrier then wake it.
synchronized (this) {
Message prev = null;
Message p = mMessages;
while (p != null && (p.target != null || p.arg1 != token)) {
prev = p;
p = p.next;
}
if (p == null) {
throw new IllegalStateException("The specified message queue synchronization "
+ " barrier token has not been posted or has already been removed.");
}
final boolean needWake;
if (prev != null) {
prev.next = p.next;
needWake = false;
} else {
mMessages = p.next;
needWake = mMessages == null || mMessages.target != null;
}
p.recycleUnchecked();
// If the loop is quitting then it is already awake.
// We can assume mPtr != 0 when mQuitting is false.
if (needWake && !mQuitting) {
nativeWake(mPtr);
}
}
}
移除屏障的方法则首先通
总结
在系统启动、应用启动时,会首先激活主线程的Looper。每个线程最多只能存在一个活跃的Looper,保存在静态遍历ThreadLocal中,且该Looper必须调用Looper.loop()方法。
在Looper.loop()方法中,通过死循环不断的从保存在Looper中的MessageQueue中获取消息,并分发到目标Handler中执行。
MessageQueue中维护着一个单链表,链表通过Message的when属性进行排序。当发送新消息时则执行链表插入。直到该msg被loop循环选中出队。

4127

被折叠的 条评论
为什么被折叠?



