Android源码阅读:Handler机制
作者:访客发布时间:2023-12-26分类:程序开发学习浏览:118
本文基于android-13.0.0_r1,源码参考: cs.android.com/android/pla…
一、Handler机制概述
接触Android的人都会很快使用到Android中的Handler机制,最常见的应用场景是子线程更新UI线程的问题。
Handler机制是一种生产者-消费者模型,其中Handler作为生产者,将消息发送到MessageQueue,MessageQueue底层是由链表实现的,Looper作为消费者,源源不断地从MessageQueue中拉取新的消息。注意Looper虽然是消费者的角色但自身并不对消息做处理,可以理解为Looper代表了消息的目标线程,其拉取消息后分发到对应的Handler(其实就是发送消息的Handler)去处理,这个过程实现了跨线程处理。
二、Looper和Handler的构造
1、Looper的构造
要想让一个普通线程能够使用Handler机制处理消息,必须先调用Looper.prepare(),再调用Looper.loop()
frameworks/base/core/java/android/os/Looper.java
public final class Looper {
final MessageQueue mQueue;
final Thread mThread;
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
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源码中可以看到,prepare函数实际上是利用ThreadLocal在调用的线程中构造一个独一无二的Looper。
而在Looper的构造函数中,会创建一个新的MessageQueue。
因此可见,一个Looper对应一个MessageQueue,一个线程有且仅能有一个Looper,也仅能有一个MessageQueue。
2、Handler的构造
Handler的构造可以指定Looper并保存在mLooper中,代表了使用这个Handler发出的消息,能够在指定Looper的线程中被处理。
public class Handler {
final Looper mLooper;
final MessageQueue mQueue;
public Handler() {
this(null, false);
}
public Handler(@Nullable Callback callback, boolean async) {
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;
}
public Handler(@NonNull Looper looper) {
this(looper, null, false);
}
public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
缺省情况下mLooper通过Looper.myLooper()获取,实际是获取当前线程的Looper。
frameworks/base/core/java/android/os/Looper.java
/**
* Return the Looper object associated with the current thread. Returns
* null if the calling thread is not associated with a Looper.
*/
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
因此可知,一个Handler只对应一个Looper,但多个Handler可以用同一个Looper初始化,即一个Looper可以对应多个Handler,一个线程也可以有多个Handler。
综上,可以有多个生产者(Handler),多个生产者可以向同一个MessageQueue发送消息。
但由于MessageQueue是随Looper创建而创建的,和Looper一一对应,Handler机制通过ThreadLocal限制了一个线程仅能有一个Looper,因此一个MessageQueue也仅能有一个Looper消费消息。
一中有讲Looper虽然作为消费者,但并不处理消息,而是分发到Handler去处理,这又是怎样的过程呢?需要从发送消息看起。
三、Handler发送消息后的流程
1、Handler发送消息
Handler可以通过post或者sendMessage系列函数发送消息,最终都是调用到sendMessageAtTime函数
frameworks/base/core/java/android/os/Handler.java
public final boolean post(@NonNull Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}
public final boolean postAtTime(@NonNull Runnable r, long uptimeMillis) {
return sendMessageAtTime(getPostMessage(r), uptimeMillis);
}
public final boolean sendMessage(@NonNull Message msg) {
return sendMessageDelayed(msg, 0);
}
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(@NonNull 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);
}
而sendMessageAtTime函数则是调用enqueueMessage向Handler对应的队列插入消息
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);
}
Handler的enqueueMessage函数有一个操作值得注意,就是将自身引用保存到msg.target,这里为之后消息的分发埋下伏笔。
最终调用MessageQueue的enqueueMessage函数。
2、消息插入MessageQueue
MessageQueue是使用链表实现的。
代码略去部分内容,核心逻辑是根据目标时间将消息插入到MessageQueue中正确的位置。
frameworks/base/core/java/android/os/MessageQueue.java
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target."); // msg必须指定Handler
}
synchronized (this) {
msg.markInUse();
msg.when = when; // when是对应的upTime,注意这里是upTime,不包括idle时间
Message p = mMessages; // mMessages指向消息队列链表第一个元素,这里给赋值给临时变量p
boolean needWake;
if (p == null || when == 0 || when < p.when) { // 链表为空,或者新msg目标时间比较早的情况下
// New head, wake up the event queue if blocked.
msg.next = p; // 新来的msg直接放在链表头
mMessages = msg;
needWake = mBlocked;
} else {
// 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(); // p.target是msg对应的 Handler
Message prev;
for (;;) {
prev = p; // 从链表头部开始遍历
p = p.next;
if (p == null || when < p.when) { // 根据msg的目标时间找到正确位置 断开链表,msg应该放在 prev和p的中间
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) {
nativeWake(mPtr); // mPtr是一个给native代码用的Long
}
}
return true;
}
至此,可以知道Handler发送消息之后,就是将消息根据目标执行时间,插入到MessageQueue中正确的位置。
那么消息是怎么被处理的呢?
四、Looper处理消息的流程
1、消息的获取
前面有说线程使用Looper必须先调用Looper.prepare(),再调用Looper.loop()
loop函数就是用来循环从MessageQueue中取出消息的,代码核心逻辑可以简化为:
frameworks/base/core/java/android/os/Looper.java
public static void loop() {
final Looper me = myLooper();
for (;;) {
if (!loopOnce(me, ident, thresholdOverride)) {
return;
}
}
}
private static boolean loopOnce(final Looper me,
final long ident, final int thresholdOverride) {
Message msg = me.mQueue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return false;
}
try {
msg.target.dispatchMessage(msg);
}
}
loop()函数中主要是循环调用loopOnce直到返回false,而在loopOnce函数中则是通过MessageQueue的next函数获取消息,再进行分发。
next()的核心逻辑可以简化为:
frameworks/base/core/java/android/os/MessageQueue.java
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) {
// 这里条件是 队首 的msg没有设置target,也就是没有Handler?
// 前面看enqueueMessage确认进来的msg都是有target的,那么这个没有target的msg怎么进来的呢?
// Stalled by a barrier. Find the next asynchronous message in the queue.
// 被barrier拦住了。 查找队列中的下一条异步消息。
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
// 到这里,msg变成了队列中的第一条异步消息
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; // 把msg从队列中取出,然后把 前一个prev和下一条 重新连接起来
} else {
mMessages = msg.next; // 没有prev,那么队首就等于msg的下一条
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg; // 这里可以理解为,只要msg的目标时间,小于等于now,那么就会被Looper用Msg Queue的next()取出来
}
}
}
}
}
next()函数中,获取当前的upTime,消息队列中的消息经过遍历,早于当前时间的消息被从链表中取出,时间未到则进入休眠等待唤醒。
2、消息的分发和处理
loopOnce函数中,获取的消息会调用msg.target.dispatchMessage(msg),在消息入队时,Handler将自身引用保存到了消息的target中,因此这里调用的是:
frameworks/base/core/java/android/os/Handler.java
/**
* 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);
}
}
在消息存在callback时调用handleCallback,否则看Handler创建时是否指定Callback,均没有则调用handleMessage进行处理。消息的处理通常是实现一个Handler子类并重写handleMessage函数,或者实现Callback接口。
frameworks/base/core/java/android/os/Handler.java
public interface Callback {
/**
* @param msg A {@link android.os.Message Message} object
* @return True if no further handling is desired
*/
boolean handleMessage(@NonNull Message msg);
}
/**
* Subclasses must implement this to receive messages.
*/
public void handleMessage(@NonNull Message msg) {
}
相关推荐
- 轻松上手:
(三)笔记可再编辑 - 如何在iPhone,iPad和Android上使用WordPress应用程序
- 一款简单高效的Android异步框架
- [Android][NDK][Cmake]一文搞懂Android项目中的Cmake
- Android---View中的setMinWidth与setMinimumWidth的踩坑记录
- Android广播如何解决Sending non-protected broadcast问题
- 有关Android Binder面试,你未知的9个秘密
- 开启Android学习之旅-2-架构组件实现数据列表及添加(kotlin)
- Android低功耗蓝牙开发总结
- Android 通知文本颜色获取
- 程序开发学习排行
- 最近发表
-
- Wii官方美版游戏Redump全集!游戏下载索引
- 视觉链接预览最好的WordPress常用插件下载博客插件模块
- 预约日历最好的wordpress常用插件下载博客插件模块
- 测验制作人最好的WordPress常用插件下载博客插件模块
- PubNews Plus|WordPress主题博客主题下载
- 护肤品|wordpress主题博客主题下载
- 肯塔·西拉|wordpress主题博客主题下载
- 酷时间轴(水平和垂直时间轴)最好的wordpress常用插件下载博客插件模块
- 作者头像列表/阻止最好的wordPress常用插件下载博客插件模块
- Elementor Pro Forms最好的WordPress常用插件下载博客插件模块的自动完成字段