《android开发艺术探索笔记》Part10、Android的消息机制

来源:互联网 发布:天津三叶网络 编辑:程序博客网 时间:2024/05/23 01:57

Android的主要消息机制主要是指Handler的运行机制,Handler的运行需要底层MessageQueue和Loop的支持。
MessageQueue的中文翻译是消息队列,顾名思义,它的内部存贮了一组消息,比队列的形式对外提供插入和删除的工作。它的内部实现是采用单链表的数据结构来存贮消息队列。
Loop会以无限循环的形式去查找是否有新消息,如果有的话就处理消息,否则一直等待着。Loop中还有一个特殊的概念,ThreadLocal,ThreadLocal不是线程,它的作用是可以在每个线程中存贮数据。ThreadLocal可以在不同的线程中互不干扰的存贮并提供数据,通过ThreadLocal可以轻松获取每个线程的Looper。

0、按照自己整理的流程图

Created with Raphaël 2.1.0LooperLooperHandlerHandler(1)(2)(3)(4)(5)

说明:
1) 标记(1) prepare()或者prepareMainLooper()创建一个Looper对象出来
2) 标记(2) 在prepare()的时候,会调用new Looper(quit)创建Looper对象和MessageQueue,将MessageQueue与Looper绑定,而MessageQueue和Handler绑定
3) 标记(3) loop()方法,开启一个死循环,内部一直调用MessageQueue的next()方法,next方法本身里面也有一个死循环,等待消息到来之前阻塞等待
4) 标记(4) sendMessage()方法调用enqueueMessage对msg.target绑定handler,并且调用MessageQueue的enQueueMessage,将消息加入到MessageQueue中,MessageQueue的死循环发现有新数据,从MessageQueue中移除消息,并返回给Looper的loop
5) 标记(5)在loop中执行msg.target.dispatchMessage(msg),将消息从Looper转到了目标Handler中,如果Callback为空的话,直接执行handlerMessage,不为空执行mCallback.handleMessage(msg),完成了消息处理

这里在简单概述一下:

*Looper的作用实际上就是通过prepare创建出来一个Looper和MessageQueue,并且使用Looper.loop()构成一个死循环,不断的从中获取新的消息,如果没有消息,就一直阻塞着。当用户开始使用的时候Handler.sendMessage发送一条消息,实际上最终调用Handler#enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis),在这里方法中,对msg的tar进行了绑定msg.target = this,MessageQueue#enqueueMessage(msg, uptimeMillis);将消息添加到了MessageQueue,MessageQueue本身是一个单链表,在插入和删除效率比较高。
上面已经说了loop会一直阻塞,知道有新消息,在MessageQueue接收到新消息之后,会返回到Looper的loop中,在loop中执行了 msg.target.dispatchMessage(msg),将消息从Looper转到了目标Handler中,如果Callback为空的话,直接执行handlerMessage,不为空执行mCallback.handleMessage(msg),完成了消息处理*

1、Android的消息机制概述

Android的消息机制主要是指Handler,而Handler的构成实际上是Handler运行机制以及Handler所附带的MessageQueue和Looper的工作过程。Handler的主要工作是把一个任务切换到一个指定的线程去执行。因为Android只能在UI线程中访问ui,所以很多时候,需要用到Handler进行线程的转场(从任务线程切换到ui线程,方便操作ui)。

系统为什么不允许在非ui线程中访问ui呢?这是因为Android的Ui线程控制不是线程安全的,如果在多线程中并发访问可能导致ui出现不可预期的状态,而如果加同步锁的话,让ui访问逻辑变得复杂,并且会降低ui的访问效率。因此android采用了简单高效的单线程模型来处理ui。

Handler创建完成之后,这个时候其内部的Looper以及MessageQueue就可以和Handler一起协同工作了,然后通过Handler的post方法将一个Runnable传递到Handler内部的Looper中处理,也可以通过Handler的send方法发送一个消息,这个消息同样会在Looper中处理。其实post最终也是调用的send方法完成的,接下来主要看一下send的过程。

当Handler的send方法被调用时,它会调用MessageQueue的enqueueMessage方法将这个消息放入到消息队列中,然后Looper发现有新消息到来时,就会处理这个消息,最终消息中的Runnable或者Handler的handlerMessage方法就会被调用。注意Looper运行在创建Handler所在的线程中。这样Handler中的业务逻辑就被切换到创建Handler的线程中去了。

Created with Raphaël 2.1.0HandlerHandlerLooperLoopersendMessage 或者 post(runnable)loop() 取出消息

2、Android的消息机制分析

ThreadLocal

首先ThreadLocal不是用来解决共享对象的多线程访问问题的,一般情况下,通过ThreadLocal.set()到线程中的对象是该线程自己使用的对象。其他线程是不需要访问的,也访问不到。各个线程中访问的是不同的对象。

ThreadLocal是一个线程内部的数据存贮类,通过它可以在指定的线程中存贮数据,数据存贮以后,只有在指定线程中可以获取到存贮的数据,对于其他线程来说则无法获取到数据。

消息队列的工作原理

消息队列在Android中主要指MessageQueue, MessageQueue主要包含两个操作: 插入和读取。读取本身会伴随着删除操作,插入和读取对应的方法分别为enqueueMessage和next,其实enququeMessage的作用是往消息队列中插入一条消息,而next的作用是从消息队列中读取出一条消息,并将其从消息队列中移除。尽管MessageQueue叫消息队列,但是它的实现是一个单链表的数据结构,用来维护消息队列,单链表在插入和删除上比较有优势。

Looper的工作原理

Looper在Android的消息机制中扮演着消息循环的角色,具体来说就是它会不停的从MessageQueue中查看是否有新消息,如果有新消息就立即处理,否则一直阻塞在哪里。

Looper.prepare(); 在当前线程中创建一个Looper

Looper.loop(); 开启消息循环

Looper.quit(); loop立即退出

Looper.quitSafely(); 安全退出,在所有的消息执行完之后退出

Looper.prepareMainLooper(); 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        final Printer logging = me.mLogging;        if (logging != null) {            logging.println(">>>>> Dispatching to " + msg.target + " " +                    msg.callback + ": " + msg.what);        }        final long traceTag = me.mTraceTag;        if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {            Trace.traceBegin(traceTag, msg.target.getTraceName(msg));        }        try {            msg.target.dispatchMessage(msg);        } finally {            if (traceTag != 0) {                Trace.traceEnd(traceTag);            }        }        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的loop方法是一个死循环,唯一跳出循环的方式是MessageQueue的next方法返回了null。放Looper的quit方法被调用时,Looper会调用MessageQueue的quit或者quitSafely方法来通知队列退出,当消息队列被标记为退出状态时,它的next方法返回也就为null。也就是说,Looper必须退出,否则loop方法就会无限循环下去。loop方法会调用MessageQueue的next方法来获取新消息,而next是一个阻塞操作,当没有消息时,next方法会一直阻塞在哪里,这也导致loop方法一直阻塞在哪里。如果MessageQueue的next方法返回了新消息,Looper就会处理这条消息:msg.target.dispatchMessage(msg),这里的msg.target是发送这条消息的Handler对象,这样的Handler发送的消息最终又交给它的dispatchMessage方法来处理了。但是这里不同的是,Handler的dispatchMessage方法是在创建Handler时所使用的Looper中执行的,这样就成功的将代码逻辑切换到了指定的线程中去执行了。

Handler的工作原理

Handler发送消息的过程仅仅是向消息队列中插入一条消息,MessageQueue的next方法就会返回这条消息给Looper,Looper收到消息后就开始处理了,MessageQueue的next方法就会返回这条消息给Looper,Looper收到消息后就开始处理了,最终消息由Handler处理,即Handler的dispatchMessage方法会被调用,这时,Handler就进入了处理消息的阶段。而在dispatch中,调用了handlerMessage(what), 这就是是最终我们需要根据what判断进行不同的消息处理了。

3、主线程的消息循环

Android的主线程就是ActivityThread,主线程的入口为main,在main方法中系统会通过Looper.perpareMainLooper()来创建主线程的Looper以及MessageQueue,并通过Looper.loop()来开启主线程的消息循环。

public static void main(String[] args) {    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");    SamplingProfilerIntegration.start();    // CloseGuard defaults to true and can be quite spammy.  We    // disable it here, but selectively enable it later (via    // StrictMode) on debug builds, but using DropBox, not logs.    CloseGuard.setEnabled(false);    Environment.initForCurrentUser();    // Set the reporter for event logging in libcore    EventLogger.setReporter(new EventLoggingReporter());    // Make sure TrustedCertificateStore looks in the right place for CA certificates    final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());    TrustedCertificateStore.setDefaultUserDirectory(configDir);    Process.setArgV0("<pre-initialized>");    Looper.prepareMainLooper();    ActivityThread thread = new ActivityThread();    thread.attach(false);    if (sMainThreadHandler == null) {        sMainThreadHandler = thread.getHandler();    }    ...    Looper.loop();    throw new RuntimeException("Main thread loop unexpectedly exited");}

主线程的消息循环开始后,ActivityThread还需要一个Handler来和消息队列进行交互,这个Handler就是ActivityThread.H。
ActivityThread通过ApplicationThread和AMS进行进程间通信,AMS以进程间通信的方式完成ActivityThread的请求后回调ApplicationThread中的Binder方法,然后ApplicationThread会向H发送消息,H收到消息后会将ApplicationThread中的逻辑切换到ActivitThread中去执行,即切换到主线程去执行,这个过程就是主线程的消息循环模型。

阅读全文
0 0