在上一篇关于 Android Handler使用方法 的文章中学会了 Handler 的使用方法,然而又是知其然不知其所以然的状态,这次较深入的学习一下 Handler 的源码。
在分析源码之前,先来了解一下Message、MessageQueue、Looper这几个对象。
定义:是线程间通讯的数据单元,包含着描述信息及任意数据对象,发送到 Handler。
在实际使用中,我们在工作线程中通过 Handler.sendMessage(Message),将携带数据的 Message 信息发送给 Handler,然后再由 Handler 处理,根据不同的信息,通知主线程作出相对应的 UI 工作。
官方文档说明:
/**
* 定义包含着描述信息及任意数据对象的可发送到 Handler 的信息。额外包含可以不被分配的两个 int 字段及一个 Object 字段。
* 获取 Message 对象最好的方法是调用 Message.obtain() 或 调用 Handler.obtainMessage() 方法来获取,调用该方法将从可回收的对象池中获取对象。
*/
public final class Message implements Parcelable {
/**
* 用户定义的消息代码,以便接收者能够识别此消息。每个 Handler 都有自己的消息代码命名空间,因此不必担心你的 Handler 与其他 Handler 起冲突。
*/
public int what;
/**
* 如果只需要存储几个整数值,以 arg1 和 arg2 为变量去使用 setData(Bundle) 是较低成本的替代选择方法。
*/
public int arg1;
public int arg2;
/**
* 发送给接收者的任意对象,当使用 Messager 跨进程发送消息时,如果包含着可序列化的框架类时,Message必须是非空的。使用setData()来传输数据。
* 注意,Android 2.2发行版之前不支持此处的Parcelable对象。
*/
public Object obj;
···略···
定义:用来存储 Message 的数据队列。
官方文档说明:
/**
* 包含着一系列由 Looper 分发的 Message 的一个低级类。
* Message 不是直接添加到 MessageQueue 中的,而是通过与 Looper 关联的 Handler 对象来添加的。
* 你可以使用“loop.myQueue()”方法来获取当前线程的 MessageQueue 对象。
*/
public final class MessageQueue {
private static final String TAG = "MessageQueue";
···略···
}
定义:用于为线程执行消息循环的一个类。是 MessageQueue 与 Handler 之间的通讯媒介。
官方文档说明:
/**
* 用于为线程执行消息循环的一个类。
* 线程默认情况下没有与之关联的消息循环;要创建一个,在将要运行循环的线程中调用 Looper.prepare(),然后调用 Looper.loop() 让它处理消息,直到循环停止。
* 与消息循环的大多数交互都是通过 Handler 类进行的。
* 这是一个典型的 Looper 线程实现的例子,使用 Looper.prepare() 和 Looper.loop() 方法创建一个 Handler 对象与 Looper 进行通信。
*
* class LooperThread extends Thread {
* public Handler mHandler;
*
* public void run() {
* Looper.prepare();
*
* mHandler = new Handler() {
* public void handleMessage(Message msg) {
* // 在这里处理传入的消息
* }
* };
*
* Looper.loop();
* }
* }
*/
public final class Looper {
/*
* API 实现注意事项:
*
* 该类包含设置和管理基于 MessageQueue 的事件循环所需的代码。
* 影响队列状态的api应该在 MessageQueue 或 Handler 上定义,而不是在 Looper 本身上定义。
* 例如,在队列上定义空闲处理程序和同步屏障,而在 Looper 上定义线程准备、循环和退出。
*/
private static final String TAG = "Looper";
···略···
}
一句话概括:
存储在 MessageQueue 中的 Message 被 Looper 循环分发到指定的 Handler 中进行处理。
关于Handler的通信机制工作原理,请看 Carson_Ho大佬的 Android Handler:图文解析 Handler通信机制 的工作原理 写的超棒,图文解析,一目了然。引用其中一Handler通信流程示意图,如下:
Thread、Handler、Looper三者之间的数量对应关系;
根据上一篇博客中介绍的Android Handler 使用方法, 我们针对 Handler.sendMessage(msg) 方法展开分析,分为3步:
创建一个 Handler 对象,也就是实例化一个 Handler 对象,在实际的使用中,我也发现有存在三种情况,分别是:
我将一一展开分析:
如下,我们在主线程中新建 Handler 对象,
//在主线程中新建 Handler 对象,并提供应用程序的 主Looper
mHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//消息处理
}
};
通过上述代码,我们创建了一个 Handler 对象,并关联应用程序的 主Looper 对象。
这个时候我们看一下 Handler类 的构造函数,如下所示:
/**
* Use the provided {@link Looper} instead of the default one.
* 使用提供的 Looper对象, 而不是使用默认的Looper对象
* @param Looper 不能为 null
*/
public Handler(@NonNull Looper looper) {
this(looper, null, false);
}
可以发现,我们提供给 Handler 的 Looper 是通过 Looper.getMainLooper() 获取的。往下看:
/**
* 返回应用程序的 主Looper, 它存在于应用程序的主线程中。
*/
public static Looper getMainLooper() {
synchronized (Looper.class) {
//返回sMainLooper, 下一步看看sMainLooper是怎么创建 Looper 对象的。
return sMainLooper;
}
}
如上所诉,getMainLooper() 返回 全局变量 sMainLooper 对象,所以我们需要看 sMainLooper 是什么时候被赋值的,如下所示,在 prepareMainLooper() 方法中,sMainLooper 被 myLooper 赋值:
/**
* 将当前线程初始化为 Looper,并将其标记为应用程序的 主Looper
* 应用程序的 主Looper 是由 Android环境 创建的,所以永远不需要自己调用这个函数
*/
public static void prepareMainLooper() {
//调用 prepare() 方法,如果当前线程没有 Looper 对象,就为之新创建一个 Looper 对象。
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
//返回当前线程所关联的 Looper 对象。
sMainLooper = myLooper();
}
}
上一步 prepareMainLooper 中:调用了 prepare(false) 来创建 Looper 对象,具体如下:
private static void prepare(boolean quitAllowed) {
//sThreadLocal.get() 返回 Looper || null,如果返回 Looper 对象,即代表已经为该线程创建了 Looper 对象,
//从而抛出异常,提示:“每个线程只能创建一个Looper”
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
//否则,调用 new Looper()方法新建一个 Looper 对象,并放入 sThreadLocal 变量中。
sThreadLocal.set(new Looper(quitAllowed)); //quitAllowed = false
}
上一步 prepare() 中:调用了 new Looper() 方法来新创建 Looper 对象,具体如下:
private Looper(boolean quitAllowed) {
//新建一个 MessageQueue
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
在 prepareMainLooper() 方法中,如果 sMainLooper 为 null,就赋值 sMainLooper = myLooper(); , myLooper() 方法如下:
/**
* 返回当前线程所关联的 Looper 对象,如果所调用的线程没有关联的 Looper 对象,就返回 null
*/
public static @Nullable Looper myLooper() {
//获取的 Looper 对象,是在 prepare() 方法中通过 sThreadLocal.set(new Looper(quitAllowed)); 所创建的
return sThreadLocal.get();
}
上述代码中,getMainLooper() 方法中的 sMainLooper 是通过 prepareMainLooper() 进行赋值的,具体代码分析如上所述,其流程图如下所示:
上述创建 Handler 对象的方法是 new Handler(Looper.getMainLooper()) ,通过 Looper.getMainLooper() 方法获取 Looper 对象,然后传入到 Handler 的构造函数中,构成绑定关联关系。
但在实际开发中,我们通常将 Handler 写成静态内部类的形式,如下:
//创建 Handler 对象
mHandler = new MyHandler(this);
/**
* 将 Handler 写成静态内部类,防止内存泄露
*/
public static class MyHandler extends Handler {
WeakReference<HandlerAddThreadActivity> weakReference;
public MyHandler(HandlerAddThreadActivity activity) {
weakReference = new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//处理收到的信息
}
}
思考: 疑问?根据上面的代码,我们没有传递 Looper 对象给 Handler 去绑定关联, 那 Handler 为何又能正常工作?
带着疑惑上路:
首先看一下 Handler类 的默认构造函数
/**
* 默认构造函数将这个处理程序与当前线程的{@link Looper}关联起来。
* 如果此线程没有looper,则此处理程序将无法接收消息,因此将引发异常。
*/
public Handler() {
this(null, false);
}
public Handler(@Nullable Callback callback, boolean async) {
···略···
//返回与当前线程关联的Looper对象。如果调用线程没有关联的 Looper 对象,则返回null
mLooper = Looper.myLooper();
//如果 mLooper 为null,则抛出异常。
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
//获取该 Looper 的 MessageQueue
mQueue = mLooper.mQueue;
//callback = null && async = false
mCallback = callback;
mAsynchronous = async;
}
看着不明显,mLooper 是通过 Looper.myLooper() 方法获得的.
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
但这个时候我想起了 prepareMainLooper() 方法。
/**
* 将当前线程初始化为 Looper,并将其标记为应用程序的 主Looper
* 应用程序的 主Looper 是由 Android环境 创建的,所以永远不需要自己调用这个函数
*/
public static void prepareMainLooper() {
//调用 prepare() 方法,如果当前线程没有 Looper 对象,就为之新创建一个 Looper 对象。
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
//返回当前线程所关联的 Looper 对象。
sMainLooper = myLooper();
}
}
疑问解除,无论我们有没有手动添加 Looper 给 Handler, Android环境都会为我们自动创建一个主线程的主Looper对象。
总结: 我们创建的 MyHandler 静态内部类,其实也是在主线程上创建的, Android环境会为我们自动创建一个应用程序的 主Looper 对象,主线程与 主Looper 绑定关联。
ok, 解除了刚刚的疑问,我又问自己,那在子线程创建 Handler 对象呢?
当然,如果你在子线程中新创建一个 Handler 对象,创建的方法为:
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
//为当前线程创建一个 Looper
Looper.prepare();
//在子线程中创建 Handler 对象
mHandler = new Handler() {
public void handleMessage(Message msg) {
// 在这里处理传入的消息
}
};
//开始消息循环
Looper.loop();
}
}
在子线程上,我们通过调用 prepare() 来为当前线程创建一个 Looper 对象
/** 将当前线程初始化为 Looper 对象,在实际开始循环之前创建并引用该 Looper 对象。
* 请确保在调用此方法后调用 loop() 方法,并通过调用 quit() 结束它
*/
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));
}
并且在 Handler 与 Looper 构成绑定关联关系后,通过调用 Looper.loop() 方法开始消息循环。
关于创建 Message 对象有两种创建方法,分别是:
/**
* 从全局池返回一个新的消息实例。允许我们在许多情况下避免分配新对象。
*/
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
//如果消息池为空,会新建一个 Message 实例
return new Message();
}
所以获取Message最佳方法是调用 Message.obtain() 或 Handler.obtainMessage() 方法,这将从回收对象池中复用消息实例,节省系统资源。
在工作线程中通过调用 mHandler.sendMessage(myMessage); 方法来发送消息,这一步是消息入消息队列操作,即将 Message 入 MessageQueue。
/**
* 在当前时间之前所有挂起的消息之后, 将消息推到消息队列的末尾。它将该线程所关联的 Handler 的 handleMessage 方法中被接收消息。
* @return 如果信息成功加入到消息队列中,返回 true,反之,入队列失败则返回 false, 失败的原因通常是因为处理消息队列的“looper”正在退出。
*/
public final boolean sendMessage(@NonNull Message msg) {
return sendMessageDelayed(msg, 0);
}
/**
* 延时 delayMillis 将消息入消息队列,它将该线程所关联的 Handler 的 handleMessage 方法中被接收消息。
* @return 如果信息成功加入到消息队列中,返回 true,反之,入队列失败则返回 false, 失败的原因通常是因为处理消息队列的 Looper 正在退出。
* 注意,true的结果并不意味着将处理该消息——如果 Looper 在消息的交付时间之前退出,则该消息将被删除。
*/
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
if (delayMillis < 0) {
//默认 delayMillis = 0
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
/**
* 在指定时间入队列,深度睡眠的时间会增加额外的执行延迟。它将该线程所关联的 Handler 的 handleMessage 方法中被接收消息。
* @param uptimeMillis 发送消息的绝对时间,使用{SystemClock # uptimeMillis}获取。
* @return 如果信息成功加入到消息队列中,返回 true,反之,入队列失败则返回 false, 失败的原因通常是因为处理消息队列的 Looper 正在退出。
* 注意,true的结果并不意味着将处理该消息——如果 Looper 在消息的交付时间之前退出,则该消息将被删除。
*/
public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
//获取当前 Looper 对象的消息队列
MessageQueue queue = mQueue;
//如果当前 Looper 对象没有消息队列,则抛出异常,返回 false, 代表消息入队列失败
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);
}
//将消息入消息队列
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);
}
根据消息的处理时间入消息队列指定位置(采用单链表实现:提高插入、删除消息的效率)
boolean enqueueMessage(Message msg, long when) {
//获得当前的 Handler 对象,如果为 null, 则抛出异常
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
//如果消息已经被使用,即已经入队列,则抛出异常
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) {
//如果消息队列已经停止退出,抛出异常,入队列失败,返回false
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
//将消息标记为正在使用
msg.markInUse();
//消息指定处理时间
msg.when = when;
Message p = mMessages;
boolean needWake;
//如果队列里没有消息,或者新消息的处理时间排在最前,即作为消息队列新的队头插入消息队列
if (p == null || when == 0 || when < p.when) {
//作为消息队列中新的头部,如果队列是 blocked 的状态则需要唤醒该事件队列。
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
//根据消息处理时间插入到队列的中间。通常我们不需要唤醒事件队列,除非是队列被阻止无法正常next(),并且消息是队列中最早的异步消息
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;
}
}
//将新消息添加到p消息的前面
msg.next = p;
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
消息入 MessageQueue 队列后,由 Looper 循环消息队列,然后派发消息,如下所示:
/**
* 在线程中运行消息队列,并确保调用 quit() 方法来结束消息循环
*/
public static void loop() {
//获取当前线程关联的 Looper 对象,如果该线程没有关联的 Looper 对象,则抛出异常
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
//获取该 Looper 对象的消息队列
final MessageQueue queue = me.mQueue;
//遍历循环消息队列
for (;;) {
//取出消息队列中的消息对象,如果消息队列为空,则 block 该线程。
Message msg = queue.next(); // might block
if (msg == null) {
//没有消息则表明消息队列已退出,结束消息循环
return;
}
···略···
try {
//获取消息对应的 Handler对象,开始分发消息
msg.target.dispatchMessage(msg);
}
//回收正在使用的消息
msg.recycleUnchecked();
}
}
/**
* Handler 处理经 Looper 分发来的消息
*/
public void dispatchMessage(@NonNull Message msg) {
//Message 中的 callback 不为空,说明是使用 Handler.post(Runnable r) 方法发送的消息(具体看 3.5Handler.post(Runnalbe)方法)
if (msg.callback != null) {
handleCallback(msg);
} else {
//注意! 这里又有一个callback,而且是一个全局变量mCallback,这个是Handler(Callback callback, ..)构造函数中传入的, 如果你没有传入自定义的callback, 这里就为null
if (mCallback != null) {
//如果实例化Handler时有传入自定义的callback方法,这里会触发该自定义方法
if (mCallback.handleMessage(msg)) {
return;
}
}
//排除上述条件,这里走的就是Handler.sendMessage(msg) 方法,回到我们覆写的 handlerMessage()方法
handleMessage(msg);
}
}
/**
* 回收可能正在使用的消息,由 MessageQueue 和 Looper 在处理排队消息时在内部使用。
*/
void recycleUnchecked() {
//将消息标记为正在使用,并将该消息的所有参数都重置。
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = UID_NONE;
workSourceUid = UID_NONE;
when = 0;
target = null;
callback = null;
data = null;
//如果消息池的数量小于最大数量(50)的话,就当前消息插入缓存池的头结点中。
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
Looper 消息循环,发送消息给指定的 Handler中(msg.target -> 指定的Handler),触发我们覆写的 handleMessage(Message) 方法。
这里补充一下关于 Handler.post(Runnable r) 方法。
/**
* 将 Runnable 添加到消息队列中,runnable 会在该Handler关联的线程上执行。
* @param r 将被执行的Runnable
*
* @return 如果添加到队列成功返回true, 反之,返回false
*/
public final boolean post(@NonNull Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
//获取一个消息实例,将 Runnable 赋值给 message 的 callback
Message m = Message.obtain();
m.callback = r;
return m;
}
剩下的步骤就跟 sendMessage()是一样的,入消息队列后,looper 循环消息队列,对消息进行处理派发到各自对应的Handler来处理消息。
public void dispatchMessage(@NonNull Message msg) {
//callback 不为空,说明是使用 Handler.post(Runnable r) 方法发送的消息
if (msg.callback != null) {
handleCallback(msg);
} else {
//注意! 这里又有一个callback,而且是一个全局变量mCallback,这个是Handler(Callback callback, ..)构造函数中传入的, 如果你没有传入自定义的callback, 这里就为null
if (mCallback != null) {
//如果实例化Handler时有传入自定义的callback方法,这里会触发该自定义方法
if (mCallback.handleMessage(msg)) {
return;
}
}
//排除上述条件,这里走的就是Handler.sendMessage(msg) 方法,回到我们覆写的 handlerMessage()方法
handleMessage(msg);
}
}
至此,关于 Android Handler.sendMessage() 的源码就分析完了。
其实分享文章的最大目的正是等待着有人指出我的错误,如果你发现哪里有错误,请毫无保留的指出即可,虚心请教。
另外,如果你觉得文章不错,对你有所帮助,请给我点个赞,就当鼓励,谢谢~Peace~!