Handler消息机制源码学习笔记
Handler机制核心解析 本文深入分析了Android Handler消息机制的核心实现: 创建过程:主线程Looper在ActivityThread.main()中初始化,通过Looper.prepareMainLooper()和loop()启动消息循环 Looper初始化: 使用ThreadLocal保证线程隔离 prepare()方法防止重复创建 构造方法创建MessageQueue和绑定
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消息循环机制完成
总结
在系统启动、应用启动时,会首先激活主线程的Looper。每个线程最多只能存在一个活跃的Looper,保存在静态遍历ThreadLocal中,且该Looper必须调用Looper.loop()
方法。
在Looper.loop()
方法中,通过死循环不断的从保存在Looper中的MessageQueue中获取消息,并分发到目标Handler中执行。
MessageQueue中维护着一个单链表,链表通过Message的when
属性进行排序。当发送新消息时则执行链表插入。直到该msg被loop循环选中出队。
更多推荐
所有评论(0)