《android开发艺术探索笔记》Part10、Android的消息机制
来源:互联网 发布:天津三叶网络 编辑:程序博客网 时间:2024/05/23 01:57
Android的主要消息机制主要是指Handler的运行机制,Handler的运行需要底层MessageQueue和Loop的支持。
MessageQueue的中文翻译是消息队列,顾名思义,它的内部存贮了一组消息,比队列的形式对外提供插入和删除的工作。它的内部实现是采用单链表的数据结构来存贮消息队列。
Loop会以无限循环的形式去查找是否有新消息,如果有的话就处理消息,否则一直等待着。Loop中还有一个特殊的概念,ThreadLocal,ThreadLocal不是线程,它的作用是可以在每个线程中存贮数据。ThreadLocal可以在不同的线程中互不干扰的存贮并提供数据,通过ThreadLocal可以轻松获取每个线程的Looper。
0、按照自己整理的流程图
说明:
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的线程中去了。
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中去执行,即切换到主线程去执行,这个过程就是主线程的消息循环模型。
- 《android开发艺术探索笔记》Part10、Android的消息机制
- 《Android开发艺术探索》笔记(2)Android消息机制
- 《Android开发艺术探索——Android的消息机制》
- android艺术开发探索之消息机制
- 《Android开发艺术探索》--消息机制
- 开发艺术探索 -- Android中的消息机制
- 《Android 开发艺术与探索》笔记——(10)Android 的消息机制
- Android开发艺术探索阅读笔记之二:Android的消息机制
- 《Android开发艺术探索第十章读书笔记》-Android消息机制
- Android开发艺术探索读书笔记(第10章 消息机制)
- 《Android开发艺术探索》之消息机制(一)
- Android开发艺术探索学习笔记5——消息机制
- 【读书笔记】【Android开发艺术探索】第10章 Android 的消息机制
- Android 开发艺术探索——第十章 Android的消息机制
- 读书笔记:Android开发艺术探索之第10章 android的消息机制
- 《Android开发艺术探索》笔记
- 《Android开发艺术探索》笔记
- Android开发艺术探索笔记_第二章 IPC机制
- Redis集群批量删除key
- 算法设计课作业系列4——Search a 2D Matrix II
- Mac指令---持续更新
- jQuery中的text(),html(),val()的区别
- Mysql合并某一个字段,解决商品属性查询问题
- 《android开发艺术探索笔记》Part10、Android的消息机制
- squid高性能代理缓存服务器
- 字节流解析json
- [leetcode] 387. First Unique Character in a String
- C语言字符输入的EOF
- 软件实施人员入门
- hibernate注意点
- 裸板程序记录之时钟初始化-s3c2440
- cd test1