备注

原发表于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中的Handler的具体用法

android的消息处理机制

《Android 开发艺术探索》

[旧][Android] 消息处理机制的更多相关文章

  1. (转)Android消息处理机制(Handler、Looper、MessageQueue与Message)

    转自 http://www.cnblogs.com/angeldevil/p/3340644.html Android消息处理机制(Handler.Looper.MessageQueue与Messag ...

  2. 【Android 开发】: Android 消息处理机制之一: Handler 与 Message

    最近几讲内容,我们学习了Android中关于多线程的一些知识,上一讲我们讲解了异步任务 AsyncTask 的操作,Android中还提供了其他的线程操作,如Handler Message Messa ...

  3. Android消息处理机制

    Android消息处理机制 Android应用程序消息处理机制(深入到native,实际由管道实现-pipe&epoll)

  4. 解析Android消息处理机制:Handler/Thread/Looper & MessageQueue

    解析Android消息处理机制 ——Handler/Thread/Looper & MessageQueue Keywords: Android Message HandlerThread L ...

  5. Android架构分析之Android消息处理机制(二)

    作者:刘昊昱 博客:http://blog.csdn.net/liuhaoyutz Android版本号:4.4.2 在上一篇文章中我们看了一个使用Handler处理Message消息的样例,本文我们 ...

  6. Android架构分析之Android消息处理机制(一)

    作者:刘昊昱 博客:http://blog.csdn.net/liuhaoyutz Android版本号:4.4.2 在这个系列文章中我们将来分析Android消息处理机制. 本文介绍了一个使用Han ...

  7. 7.1 基础知识Android消息处理机制

    1. Android消息处理机制: Handler, MessageQueue, Looper, Thread 线程概念 : 一个应用程序运行时它的主体被称为进程, 一个进程内部可以有多个线程, 线程 ...

  8. Android消息处理机制(Handler 与Message)---01

    一.handler的使用场景为么会有handler?(部分内容图片摘自http://www.runoob.com/w3cnote/android-tutorial-handler-message.ht ...

  9. 【Android】Android消息处理机制

    三大核心类 android的消息处理有三个核心类:Looper,Handler和Message. 其实还有一个Message Queue(消息队列),但是MQ被封装到Looper里面了 Looper ...

随机推荐

  1. 【笔记】golang中使用protocol buffers的底层库直接解码二进制数据

    背景 一个简单的代理程序,发现单核QPS达到2万/s左右就上不去了,40%的CPU消耗在pb的decode/encode上面. 于是我想,对于特定的场景,直接从[]byte中取出字段,而不用完全的把整 ...

  2. 【解决了一个小问题】golang gin框架中的模板,让模板中的参数不要做HTML转义

    代码中使用了类似的方式来向模板填充参数: c.HTML(200, "list.html", gin.H{"data":builder.String()}) 模板 ...

  3. ROS之face recongination(cbo_peopel_detection)

    一准备 Ros的cbo_people_detection网站http://wiki.ros.org/cob_people_detection 某网站来自Amir:http://edu.gaitech. ...

  4. golang中的标准库IO操作

    参考链接 输入输出的底层原理 终端其实是一个文件,相关实例如下: os.Stdin:标准输入的文件实例,类型为*File os.Stdout:标准输出的文件实例,类型为*File os.Stderr: ...

  5. 使用MobaXterm发布服务器项目文件

    注:笔记旨在记录 目录 一.使用MobaXterm登录服务器 二.后端发布 三.前端发布 一.使用MobaXterm登录服务器 打开MobaXterm,点击左上角Session->SSH,输入账 ...

  6. docker常用命令、镜像命令、容器命令、数据卷,使用dockerFile创建镜像,dockefile的语法规则。

    一.docker常用命令? 1. 常用帮助命令 1.1 docker的信息以及版本号 /* docker info 查看docker的信息 images2 docker本身就是一个镜像. docker ...

  7. FreeSWITCH 对接RTSP和RTMP视频

    在某些场景需要把摄像头或者其它推流视频加入FreeSWITCH.因此可以采用如下方式处理: 安装mod_vlc 然后在配置文件中加入 < action applicaiton="pla ...

  8. JS 中的GB2312转UTF8和UTF8转GB2312

    转载请注明来源:https://www.cnblogs.com/hookjc/ JS:encodeURI encodeURI(URIString)必选的 URIString 参数代表一个已编码的 UR ...

  9. 类扩展(Class Extension)

    类扩展(Class Extension) 也有人称为匿名分类 - 作用     - 能为某个类增加额外的属性.成员变量.方法声明     - 一般将类扩展写到.m文件中     - 一般将一些私有的属 ...

  10. Copy as Markdown - 将页面链接按照 Markdown 格式copy

    将页面文字和链接组成 Markdown 格式的网址 直接对页面链接右键使用时,无法获取链接标题,只能显示 No Title 所以需要: 选中「想作为标题的部分文字」, 然后去对「页面链接」右键-> ...