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 中主要包括两个操作:插入和读取,在读取的同时伴随有删除。 插入和读取分别对应于 enqueueMessagenext。 enqueueMessage 是往队列中插入 一条数据,采用非的是单链表的插入操作,其内部采用了锁机制,而 next 是一个无限循环方法,若无消息,那么它将阻塞者,若有消息,则返回该消息并将其从消息队列中移除。

2.3 Looper 的工作原理

Looper 是消息循环的角色,不停的从 MessageQueue 中取消息,若存在则立即处理,否则阻塞。在 Looper 的构造方法中会创建一个MessageQueue对象。
Handle  的工作需要 looper ,如果没有回报错,可以用 prepare 方法创建Looper:

1// 创建looper
2Looper.prepare();
3//....
4//开启循环
5Looper.loop();

才外,还提供了 prepareMainLooper 方法为主线程创建Looper。对于退出循环,则提供了 quitquitSafely 方法,前者是直接退出,后者则是设置个退出标记,等消息处理完后再退出。
通常在子线程中创建的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 消息机制及其原理的主要内容,如果未能解决你的问题,请参考以下文章

Android的handler机制的原理?

Android进阶知识——Android的消息机制

Android进阶知识——Android的消息机制

Android进阶知识——Android的消息机制

Android进阶知识——Android的消息机制

Android Handler消息机制从原理到应用详解