[旧][Android] 消息处理机制
备注
原发表于2016.06.06,资料已过时,仅作备份,谨慎参考
概述
Android 的消息处理机制主要是指 Handler 的运行机制以及 Handler 所附带的 MessageQueue 和 Looper 的工作流程。
在 Handler 创建完毕之后,就可以通过 Handler.post 方法将一个 Runnable 转换成一个 Message 对象,它会调用 MessageQueue 的 enqueueMessage() 将其放入消息队列中:
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
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);
}
Looper 发现有新消息到来时,就会处理这个消息,最终消息中的 Runnable 或 Handler 的 handleMessage 方法会被调用。这样就切换到了创建 Handler 所在的线程中去执行了。
其工作流程图如下所示:

MessageQueue 的工作原理
MessageQueue 类主要包含两个操作:插入和读取,它通过一个单链表的数据结构来维护消息列表,在每个 Looper 中都持有一个 MessageQueue 对象。
enqueueMessage 方法主要就是往单链表中插入一条消息,我们来看一下 next 读取方法的代码:
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;
}
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
// If first time idle, then get the number of idlers to run.
// Idle handles only run if the queue is empty or if the first message
// in the queue (possibly a barrier) is due to be handled in the future.
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// Run the idle handlers.
// We only ever reach this code block during the first iteration.
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler
boolean keep = false;
try {
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
// Reset the idle handler count to 0 so we do not run them again.
pendingIdleHandlerCount = 0;
// While calling an idle handler, a new message could have been delivered
// so go back and look again for a pending message without waiting.
nextPollTimeoutMillis = 0;
}
}
尽管代码偏长,但可以看出 next 方法实际上是一个无限循环,直到 MessageQueue 中有消息则将其取出并删除,否则会一直阻塞在这里。
Looper 的工作原理
Looper 在消息处理机制中负责不断从 MessageQueue 中查看是否有新消息,如果有的话则进行处理,否则就一直阻塞在哪里。
在初始化 Looper 时会创建一个 MessageQueue:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
当调用 prepare 方法时,可当前线程初始化并绑定一个 Looper,可以看到不能重复调用 prepare 方法:
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));
}
最后我们会调用 loop 方法来开启消息循环,这也是 Looper 中最重要的一个方法,我们先列出整体代码,再来依次进行分析:
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
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
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
msg.target.dispatchMessage(msg);
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
msg.recycleUnchecked();
}
}
先取出与当前线程绑定的 Looper,并取出 Looper 中的 MessageQueue 消息队列,接着就进入了无限循环:
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
for (;;) {
后面的代码都是在无限循环之内,这里取出消息,如果为 null 则退出循环:
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
注意当 MessageQueue 没有消息时,是不会返回 null 的,只会一直循环等待消息。只有当调用 quit 方法退出时,才会返回 null。
public void quit() {
mQueue.quit(false);
}
接下来会对消息进行处理:
msg.target.dispatchMessage(msg);
msg.target 就是发送该消息的 Handler,这里就会将线程切换到该 Handler 所在的线程去处理该 msg,这样就完成了线程的切换啦~
Handler 的工作原理
在概述中我们已经讲过了,Handler.post 方法会将消息插入到 Looper 中的消息队列,开启循环后又会将该消息转发到 Handler 所在线程进行处理,那么我们就来看一下 dispatchMessage 方法:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
msg.callback 是 post 方法中传递进去的 Runnable 参数,如果不为空,则:
private static void handleCallback(Message message) {
message.callback.run();
}
如果为空,则判断 Handler 的 Callback 是否为空,这个 Callback 的定义如下:
public interface Callback {
public boolean handleMessage(Message msg);
}
我们可以用如下这种方式来创建一个 Handler:
public Handler(Callback callback) {
this(callback, false);
}
实际上是与我们重写 Handler.handleMessage 方法差不多的,只是另一种使用 Handler 的方式而已。
结语
那么以上就是,Android 的消息处理机制了,这一部分的代码比较简单易懂,所以推荐大家都去看看。
参考资料
[旧][Android] 消息处理机制的更多相关文章
- (转)Android消息处理机制(Handler、Looper、MessageQueue与Message)
转自 http://www.cnblogs.com/angeldevil/p/3340644.html Android消息处理机制(Handler.Looper.MessageQueue与Messag ...
- 【Android 开发】: Android 消息处理机制之一: Handler 与 Message
最近几讲内容,我们学习了Android中关于多线程的一些知识,上一讲我们讲解了异步任务 AsyncTask 的操作,Android中还提供了其他的线程操作,如Handler Message Messa ...
- Android消息处理机制
Android消息处理机制 Android应用程序消息处理机制(深入到native,实际由管道实现-pipe&epoll)
- 解析Android消息处理机制:Handler/Thread/Looper & MessageQueue
解析Android消息处理机制 ——Handler/Thread/Looper & MessageQueue Keywords: Android Message HandlerThread L ...
- Android架构分析之Android消息处理机制(二)
作者:刘昊昱 博客:http://blog.csdn.net/liuhaoyutz Android版本号:4.4.2 在上一篇文章中我们看了一个使用Handler处理Message消息的样例,本文我们 ...
- Android架构分析之Android消息处理机制(一)
作者:刘昊昱 博客:http://blog.csdn.net/liuhaoyutz Android版本号:4.4.2 在这个系列文章中我们将来分析Android消息处理机制. 本文介绍了一个使用Han ...
- 7.1 基础知识Android消息处理机制
1. Android消息处理机制: Handler, MessageQueue, Looper, Thread 线程概念 : 一个应用程序运行时它的主体被称为进程, 一个进程内部可以有多个线程, 线程 ...
- Android消息处理机制(Handler 与Message)---01
一.handler的使用场景为么会有handler?(部分内容图片摘自http://www.runoob.com/w3cnote/android-tutorial-handler-message.ht ...
- 【Android】Android消息处理机制
三大核心类 android的消息处理有三个核心类:Looper,Handler和Message. 其实还有一个Message Queue(消息队列),但是MQ被封装到Looper里面了 Looper ...
随机推荐
- Solon Web 开发,四、请求上下文
Solon Web 开发 一.开始 二.开发知识准备 三.打包与运行 四.请求上下文 五.数据访问.事务与缓存应用 六.过滤器.处理.拦截器 七.视图模板与Mvc注解 八.校验.及定制与扩展 九.跨域 ...
- Api自动生成
如果经常对接api, 可以自己写一个自动化生成代码,提高效率 只抛出一个思路,暂不提供源码 使用json+字符串处理+生成文件 发送一个请求,返回字符串转换为 Newtonsoft.Json.Linq ...
- 【记录一个问题】golang中copy []byte类型的slice无效,为什么?
有这样一段代码: src := []byte{xxxxx} dst := make([]byte, 0, len(src)) copy(dst, src) //这一行居然没生效! // dst = a ...
- 发现一个现象:golang中大量的go出新协程,必然在GC统计中出现1ms以上的GC延迟
结论:协程池还是有必要的,能够有效减小GC的压力. 我的某个服务,为了方(tou)便(lan),一些异步处理的场合直接go出协程来处理. 服务中使用这样的代码来统计GC的延迟: var mem run ...
- MacBook USB 连 iPhone 响个不停,充电频繁的连上/断开
电脑是 MacBook Pro (Retina, 15-inch, Mid 2015) 连 iPhone 响个不停,充电频繁的连上 /断开,数据线是原装的,想连接爱思助手连接不上. 网上方法 1 开启 ...
- 3D建模服务提供更高效、专业的能力,“筑”力开发者
3D建模服务(3D Modeling Kit)是HMS Core在图形图像领域又一技术开放.3D建模产品的定位就是要做快速.简洁.低成本的3D制作能力,并陆续开放给有3D模型.动画游戏制作等能力诉求的 ...
- JavaScript之详述闭包导致的内存泄露
一.内存泄露 1. 定义:一块被分配的内存既不能使用,也不能回收.从而影响性能,甚至导致程序崩溃. 2. 起因:JavaScript的垃圾自动回收机制会按一定的策略找出那些不再继续使用的变量,释放其占 ...
- 笔记:Bridging the Gap Between Relevance Matching and Semantic Matching for Short Text Similarity Modeling
笔记:Bridging the Gap Between Relevance Matching and Semantic Matching for Short Text Similarity Model ...
- JavaScript通过父节点ID递归生成JSON树
JavaScript通过父节点ID递归生成JSON树: · 实现思路:通过递归实现(第一次递归的时候查询出所有的父节点,然后通过当前父节点id不断地去查询所有子节点,直到递归完毕返回) · 代码示 ...
- Python之基本数据类型与数据结构
一.基础数据类型 标准数据类型: ·不可变数据类型 Number(数字):int.float.bool.complex(复数) String(字符串) Tuple(元祖):不可变,无法通过下标来修改值 ...