老罗[android源代码分析] 读书笔记-安卓的消息队列

来源:互联网 发布:windows dream scence 编辑:程序博客网 时间:2024/06/04 23:27

首先,消息循环涉及3个对象,Looper,MessageQueue和Handler。其中,Handler负责往消息队列中添加消息以及从队列中获取消息并处理。Looper类负责启动消息循环,MessageQueue即消息队列。
android应用程序中的每个线程都可以拥有一个Looper对象,只需要在线程中调用Looper.prepare()就可以了。安卓应用程序在启动的时候,会在主线程中调用Looper.prepareMainLooper()启动主线程的消息循环。Looper类中有一个静态成员变量ThreadLocal,用来保存每个线程的Looper对象,并且把mainLooper保存在一个静态变量中,使得每个线程都可以使用主线程的消息队列。
android中的Looper,MessageQueue两个类又和C++层的Looper和MessageQueue关联。当java层的Looper和MessageQueue被创建时,也会使用本地方法创建C++的Looper和MessageQueue对象,并且使用一个指针把java层的MessageQueue和C++层的MessageQueue关联起来。C++层的Looper对象在创建过程中,会在内部创建一个管道,管道使用两个文件描述符来维护,一个是写端文件描述符,另一个是读端文件描述符。写端文件负责向消息队列添加消息,而读端文件则可以从管道中读取消息。管道在线程消息循环中作用很大,当消息队列中没有消息时,线程将会在睡眠在管道的读短文件描述符上,直到有新的消息加入。其他线程要向本线程的消息队列post消息时,只需要通过这个写端文件描述符即可向本进程的消息队列添加消息。
android会在C++层创建一个epoll实例,并把管道的读端描述符添加到这个实例中,通过这个epoll实例来监听管道的写操作。

Looper调用loop函数进入一个while(true)的消息循环,当队列中没有消息时,线程将会阻塞。loop函数涉及到一个本地函数,在该本地函数中,会调用epoll_wait函数,检查注册在epoll实例中的写文件描述符是否有写事件发生,如果没有,则线程进入休眠。接下来会遍历这些发生写事件的文件描述符,如果文件描述符和当前线程的写事件文件描述符一样,那么就从这个管道中读取消息。

Handler被创建时,会把当前线程的looper对象以及messageQueue对象保存到它的成员变量中,所以如果当前线程没有调用Looper.prepare,将会报错。或者给Handler的构造函数提供一个Looper对象。
当调用sendMessage时,会把Message的Handler变量指向本Handler,然后加入到MessageQueue中。消息队列中的消息按事件顺序来排列,越早的消息离队列头部越近。然后根据这个消息的位置,来决定是否唤醒该线程。

唤醒的函数会调用一个本地函数,而该本地函数会通过向该线程所拥有的Looper中的管道写入消息,来唤醒这个线程。

当Looper从队列中取出消息后,会调用消息的Handler变量的dispatch函数,开始处理消息。消息有分为两种消息,1.sendMessage函数添加的消息 2.postRunnable函数添加的消息。第一种消息添加到消息队列之后,会调用Handler的handleMessage函数来处理消息,第二种消息则会运行runnable对象的run函数。

 

0 0
原创粉丝点击