android 消息机制及其原理
Posted 奚岩技术博客
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了android 消息机制及其原理相关的知识,希望对你有一定的参考价值。
读书笔记: 《android 开发艺术探索》 ——第十章:android 消息机制
对于 android 中的消息机制,主要是指 Handler 的运行机制。在我们平时的开发中 ,对它并不陌生。由于android 是 单线程(UI线程)机制,对于一些耗时操作会在子线程中进行,如文件读取等.
往往在操作完成后会有 UI 的更新,由于 android 中不允许在子线程中更新ui,所以我们 常常用 Handler 来更新UI ,但它的功能不仅仅如此。
一、消息机制概述
Handler
的运行 需要底层的 MessageQueue 和 Looper 支撑。MessageQueue
是指消息队列,在他内部存储了一组消息,以队列的形式对外提供增删。虽名为队列,但是其内部的实现是采用 单链表。Looper
主要是用于消息循环,他内部通过无线循环的方式,查看是否有消息,如果有就处理,否则阻塞等待着。 在 Looper 中 利用 ThreadLocal
进行存储数据,它可以保证各个线程中互不干扰的存储和提供数据。
如果 使用 Handler 就必须为线程创建 Looper。我们能够在 Activity 只用它,主要是应为在 UI 线程( ActivityThread ) 创建是会初始化 looper。
android 系统不允许 在子线程中访问UI ,主要是 很多控件时线程不安全的,如果多线程并发访问会出现不可预期的效果;同时由于 锁机制会让 UI 访问逻辑变复杂,并且会阻塞某些线程从而降低 UI 访问效率,并没有对 UI 线程进行加锁 操作。
如下是 Handler 的 工作过程:
说明:首先 Handle 通过
sendMessage()
等方法发送一个消息,最终会调用 MessageQueue 的 enqueueMessage 方法 将消息添加到消息队列中;而 Looper 的 loop方法发现新消息后,从队列中取出消息,最后将其转发到 Handle 中,最终在 handleMessage 进行处理。而Looper 是运行在创建handler 的线程中,这样将Handler 中的业务逻辑切换到 穿件 Handler 的线程中去了。
二、消息机制分析
2.1 ThreadLocal 的工作原理
ThreadLocal 主要是线程内部的数据存储类,他可以在指定的线程中存储数据,然后只有指定的线程可以获取。,而其他线程则无法获取。这里使用它 可以方便的实现 Looper 在线程中的存取,此外,他还可以在复杂的逻辑下进行对象的传递,如监听器的传递。
由于 api23 前后,ThreadLocal 的内部实现不同,这里不具体介绍。
2.2 MessageQueue 的工作原理
在消息队列 MessageQueue 中主要包括两个操作:插入和读取,在读取的同时伴随有删除。 插入和读取分别对应于 enqueueMessage
和 next
。 enqueueMessage 是往队列中插入 一条数据,采用非的是单链表的插入操作,其内部采用了锁机制,而 next 是一个无限循环方法,若无消息,那么它将阻塞者,若有消息,则返回该消息并将其从消息队列中移除。
2.3 Looper 的工作原理
Looper 是消息循环的角色,不停的从 MessageQueue 中取消息,若存在则立即处理,否则阻塞。在 Looper 的构造方法中会创建一个MessageQueue对象。
Handle 的工作需要 looper ,如果没有回报错,可以用 prepare
方法创建Looper:
1// 创建looper
2Looper.prepare();
3//....
4//开启循环
5Looper.loop();
才外,还提供了 prepareMainLooper
方法为主线程创建Looper。对于退出循环,则提供了 quit
和 quitSafely
方法,前者是直接退出,后者则是设置个退出标记,等消息处理完后再退出。
通常在子线程中创建的looper ,在执行完后应该退出,当执行退出后,次线程会立即终止,若handler 再次发送消息,则会返回 false。
由于在 Looper 的 loop 方法中调用用了 MessageQueue 的 next方法,而next 方法是个阻塞的,导致loop阻塞。如下loop方法:
1public static void loop()
2{
3 final Looper me = myLooper();
4 if (me == null)
5 {
6 throw new RuntimeException ("No Looper; Looper.prepare() wasn't called on this thread.");
7 }
8 final MessageQueue queue = me.mQueue;
9
10 // Make sure the identity of this thread is that of the local process,
11 // and keep track of what that identity token actually is.
12 Binder.clearCallingIdentity();
13 final long ident = Binder.clearCallingIdentity();
14
15 for (;;)
16 {
17 Message msg = queue.next(); // might block
18 if (msg == null)
19 {
20 // No message indicates that the message queue is quitting.
21 return;
22 }
23
24 // This must be in a local variable, in case a UI event sets the logger
25 final Printer logging = me.mLogging;
26 //...
27
28 final long traceTag = me.mTraceTag;
29 if (traceTag != 0 && Trace.isTagEnabled (traceTag) )
30 {
31 Trace.traceBegin (traceTag, msg.target.getTraceName (msg) );
32 }
33 try
34 {
35 msg.target.dispatchMessage (msg);
36 } finally
37 {
38 if (traceTag != 0)
39 {
40 Trace.traceEnd (traceTag);
41 }
42 }
43
44 //...
45 // Make sure that during the course of dispatching the
46 // identity of the thread wasn't corrupted.
47 final long newIdent = Binder.clearCallingIdentity();
48 //...
49
50 msg.recycleUnchecked();
51 }
52}
注意
msg.target.dispatchMessage (msg);
一句,msg是一个从MessageQueue 中取出的Message对象,而 target 则是 Message 中的一个 Handler 类型的 成员变量,这样使得 loop方法将消息队列中的消息分发给 Handler 进行处理。
2.4 Handler 的工作原理
handler 主要包括消息的发送和接受,主要包括一系列的post和send方法实现的,而post最终是通过 send实现的。如下各个方法:
1 public final boolean sendMessage(Message msg)
2{
3 return sendMessageDelayed(msg, 0);
4}
5 public final boolean sendEmptyMessage(int what)
6{
7 return sendEmptyMessageDelayed(what, 0);
8}
9 public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
10 Message msg = Message.obtain();
11 msg.what = what;
12 return sendMessageDelayed(msg, delayMillis);
13}
14 public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
15 Message msg = Message.obtain();
16 msg.what = what;
17 return sendMessageAtTime(msg, uptimeMillis);
18}
19 public final boolean sendMessageDelayed(Message msg, long delayMillis)
20{
21 if (delayMillis < 0) {
22 delayMillis = 0;
23 }
24 return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
25}
26 public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
27 MessageQueue queue = mQueue;
28 if (queue == null) {
29 RuntimeException e = new RuntimeException(
30 this + " sendMessageAtTime() called with no mQueue");
31 Log.w("Looper", e.getMessage(), e);
32 return false;
33 }
34 return enqueueMessage(queue, msg, uptimeMillis);
35 }
36 private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
37 msg.target = this;
38 if (mAsynchronous) {
39 msg.setAsynchronous(true);
40 }
41 return queue.enqueueMessage(msg, uptimeMillis);
42 }
通过上面的各个方法,最终是往调用了 enqueueMessage 方法 往 MessageQueue 中插入一条消息。在Looper 中 调用了 MessageQueue 的next 方法,取出一条消息,通过 dispatchMessage 方法将消息分发给 Handler 处理,如下其具体实现:
1public void dispatchMessage(Message msg) {
2 if (msg.callback != null) {
3 handleCallback(msg);
4 } else {
5 if (mCallback != null) {
6 if (mCallback.handleMessage(msg)) {
7 return;
8 }
9 }
10 handleMessage(msg);
11 }
12}
这里首先检查 callback 是否为null,不为空就调用 handleCallback 处理,它是一个 Runnable对象;其次检查 mCallback 是否为null ,mCallback 是一个Callback类型的接口,内部只有一个方法:
1public interface Callback {
2 public boolean handleMessage(Message msg);
3}
这里的 Callback 可以用来创建 Handle 对象,常见的创建 Handler 是重写 handleMessage 方法。
如下 Handler 的消息处理流程:
三、主线程消息循环
主线程即ActivityThread ,其注入口方法为 main,在该方法中,通过 Looper.prepareMainLooper();
创建Looper,最后通过 Looper.loop();
开启消息循环。
1public static void main (String[] args)
2{
3
4 //...
5 // 创建主线程的Looper
6 Looper.prepareMainLooper();
7
8 ActivityThread thread = new ActivityThread();
9 thread.attach (false);
10
11 if (sMainThreadHandler == null)
12 {
13 sMainThreadHandler = thread.getHandler();
14 }
15
16 if (false)
17 {
18 Looper.myLooper().setMessageLogging (new
19 LogPrinter (Log.DEBUG, "ActivityThread") );
20 }
21
22 // End of event ActivityThreadMain.
23 Trace.traceEnd (Trace.TRACE_TAG_ACTIVITY_MANAGER);
24 //开启循环
25 Looper.loop();
26
27 throw new RuntimeException ("Main thread loop unexpectedly exited");
28}
ActivityThread 的内部类 H 继承自 Handler ,其内部定义了一组消息类型,组要包括了四大组件的启动和停止。
主线程消息循环模型:ActivityThread 内部通过 ApplicationThread 和 AMS 进行进程间通信,AMS 以进程间通信的方式完成 ActivityThread 的请求后回调 ApplicationThread 中的 Binder 方法,然后 ApplicationThread 向 H 发送消息, H收到后将 ApplicationThread 中的逻辑切换到 ActivityThread 中去执行。
以上是关于android 消息机制及其原理的主要内容,如果未能解决你的问题,请参考以下文章