Android应用程序消息处理机制LooperHandler分析文档格式.docx
- 文档编号:1392999
- 上传时间:2023-04-30
- 格式:DOCX
- 页数:46
- 大小:88.46KB
Android应用程序消息处理机制LooperHandler分析文档格式.docx
《Android应用程序消息处理机制LooperHandler分析文档格式.docx》由会员分享,可在线阅读,更多相关《Android应用程序消息处理机制LooperHandler分析文档格式.docx(46页珍藏版)》请在冰点文库上搜索。
main(String[]
args)
5.
6.
7.
Looper.prepareMainLooper();
8.
9.
10.
11.
thread
=
new
ActivityThread();
12.
thread.attach(false);
13.
14.
15.
16.
Looper.loop();
17.
18.
19.
20.
thread.detach();
21.
22.
23.
}
24.}
这个函数做了两件事情,一是在主线程中创建了一个ActivityThread实例,二是通过Looper类使主线程进入消息循环中,这里我们只关注后者。
首先看Looper.prepareMainLooper函数的实现,这是一个静态成员函数,定义在frameworks/base/core/java/android/os/Looper.java文件中:
Looper
private
ThreadLocal
sThreadLocal
ThreadLocal();
MessageQueue
mQueue;
/**
Initialize
the
current
as
a
looper.
*
This
gives
you
chance
to
create
handlers
that
then
reference
this
looper,
before
actually
starting
loop.
Be
sure
call
{@link
#loop()}
after
calling
method,
and
end
it
by
#quit()}.
*/
prepare()
if
(sThreadLocal.get()
!
null)
throw
RuntimeException("
Only
one
may
be
created
per
thread"
);
sThreadLocal.set(new
Looper());
marking
an
application'
s
main
24.
The
looper
for
your
application
is
Android
environment,
25.
so
should
never
need
function
yourself.
26.
#prepare()}
27.
28.
29.
prepareMainLooper()
30.
prepare();
31.
setMainLooper(myLooper());
32.
(Process.supportsProcesses())
33.
myLooper().mQueue.mQuitAllowed
false;
34.
35.
36.
37.
synchronized
setMainLooper(Looper
looper)
38.
mMainLooper
looper;
39.
40.
41.
42.
Return
object
associated
with
thread.
Returns
43.
null
not
Looper.
44.
45.
myLooper()
46.
return
(Looper)sThreadLocal.get();
47.
48.
49.
Looper()
50.
mQueue
MessageQueue();
51.
mRun
true;
52.
mThread
Thread.currentThread();
53.
54.
55.
56.}
函数prepareMainLooper做的事情其实就是在线程中创建一个Looper对象,这个Looper对象是存放在sThreadLocal成员变量里面的,成员变量sThreadLocal的类型为ThreadLocal,表示这是一个线程局部变量,即保证每一个调用了prepareMainLooper函数的线程里面都有一个独立的Looper对象。
在线程是创建Looper对象的工作是由prepare函数来完成的,而在创建Looper对象的时候,会同时创建一个消息队列MessageQueue,保存在Looper的成员变量mQueue中,后续消息就是存放在这个队列中去。
消息队列在Android应用程序消息处理机制中最重要的组件,因此,我们看看它的创建过程,即它的构造函数的实现,实现frameworks/base/core/java/android/os/MessageQueue.java文件中:
int
mPtr;
//
used
native
code
nativeInit();
MessageQueue()
13.}
它的初始化工作都交给JNI方法nativeInit来实现了,这个JNI方法定义在frameworks/base/core/jni/android_os_MessageQueue.cpp文件中:
1.static
android_os_MessageQueue_nativeInit(JNIEnv*
env,
jobject
obj)
NativeMessageQueue*
nativeMessageQueue
NativeMessageQueue();
(!
nativeMessageQueue)
jniThrowRuntimeException(env,
"
Unable
allocate
queue"
return;
android_os_MessageQueue_setNativeMessageQueue(env,
obj,
nativeMessageQueue);
9.}
在JNI中,也相应地创建了一个消息队列NativeMessageQueue,NativeMessageQueue类也是定义在frameworks/base/core/jni/android_os_MessageQueue.cpp文件中,它的创建过程如下所示:
1.NativeMessageQueue:
:
NativeMessageQueue()
mLooper
Looper:
getForThread();
(mLooper
==
NULL)
Looper(false);
setForThread(mLooper);
7.}
它主要就是在内部创建了一个Looper对象,注意,这个Looper对象是实现在JNI层的,它与上面Java层中的Looper是不一样的,不过它们是对应的,下面我们进一步分析消息循环的过程的时候,读者就会清楚地了解到它们之间的关系。
这个Looper的创建过程也很重要,不过我们暂时放一放,先分析完android_os_MessageQueue_nativeInit函数的执行,它创建了本地消息队列NativeMessageQueue对象之后,接着调用android_os_MessageQueue_setNativeMessageQueue函数来把这个消息队列对象保存在前面我们在Java层中创建的MessageQueue对象的mPtr成员变量里面:
android_os_MessageQueue_setNativeMessageQueue(JNIEnv*
messageQueueObj,
env->
SetIntField(messageQueueObj,
gMessageQueueClassInfo.mPtr,
reinterpret_cast<
jint>
(nativeMessageQueue));
5.}
这里传进来的参数messageQueueObj即为我们前面在Java层创建的消息队列对象,而gMessageQueueClassInfo.mPtr即表示在Java类MessageQueue中,其成员变量mPtr的偏移量,通过这个偏移量,就可以把这个本地消息队列对象natvieMessageQueue保存在Java层创建的消息队列对象的mPtr成员变量中,这是为了后续我们调用Java层的消息队列对象的其它成员函数进入到JNI层时,能够方便地找回它在JNI层所对应的消息队列对象。
我们再回到NativeMessageQueue的构造函数中,看看JNI层的Looper对象的创建过程,即看看它的构造函数是如何实现的,这个Looper类实现在frameworks/base/libs/utils/Looper.cpp文件中:
1.Looper:
Looper(bool
allowNonCallbacks)
mAllowNonCallbacks(allowNonCallbacks),
mResponseIndex(0)
wakeFds[2];
result
pipe(wakeFds);
mWakeReadPipeFd
wakeFds[0];
mWakeWritePipeFd
wakeFds[1];
13.#ifdef
LOOPER_USES_EPOLL
Allocate
epoll
instance
register
wake
pipe.
mEpollFd
epoll_create(EPOLL_SIZE_HINT);
struct
epoll_event
eventItem;
memset(&
eventItem,
0,
sizeof(epoll_event));
zero
out
unused
members
of
data
field
union
eventItem.events
EPOLLIN;
eventItem.data.fd
mWakeReadPipeFd;
epoll_ctl(mEpollFd,
EPOLL_CTL_ADD,
mWakeReadPipeFd,
&
eventItem);
24.#else
26.#endif
29.}
这个构造函数做的事情非常重要,它跟我们后面要介绍的应用程序主线程在消息队列中没有消息时要进入等待状态以及当消息队列有消息时要把应用程序主线程唤醒的这两个知识点息息相关。
它主要就是通过pipe系统调用来创建了一个管道了:
1.int
2.int
3.......
5.mWakeReadPipeFd
6.mWakeWritePipeFd
管道是Linux系统中的一种进程间通信机制,具体可以参考前面一篇文章Android学习启动篇推荐的一本书《Linux内核源代码情景分析》中的第6章--传统的Uinx进程间通信。
简单来说,管道就是一个文件,在管道的两端,分别是两个打开文件文件描述符,这两个打开文件描述符都是对应同一个文件,其中一个是用来读的,别一个是用来写的,一般的使用方式就是,一个线程通过读文件描述符中来读管道的内容,当管道没有内容时,这个线程就会进入等待状态,而另外一个线程通过写文件描述符来向管道中写入内容,写入内容的时候,如果另一端正有线程正在等待管道中的内容,那么这个线程就会被唤醒。
这个等待和唤醒的操作是如何进行的呢,这就要借助Linux系统中的epoll机制了。
Linux系统中的epoll机制为处理大批量句柄而作了改进的poll,是Linux下多路复用IO接口select/poll的增强版本,它能显著减少程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率。
但是这里我们其实只需要监控的IO接口只有mWakeReadPipeFd一个,即前面我们所创建的管道的读端,为什么还需要用到epoll呢?
有点用牛刀来杀鸡的味道。
其实不然,这个Looper类是非常强大的,它除了监控内部所创建的管道接口之外,还提供了addFd接口供外界面调用,外界可以通过这个接口把自己想要监控的IO事件一并加入到这个Looper对象中去,当所有这些被监控的IO接口上面有事件发生时,就会唤醒相应的线程来处理,不过这里我们只关心刚才所创建的管道的IO事件的发生。
要使用Linux系统的epoll机制,首先要通过epoll_create来创建一个epoll专用的文件描述符:
1.mEpollFd
传入的参数EPOLL_SIZE_HINT是在这个mEpollFd上能监控的最大文件描述符数。
接着还要通过epoll_ctl函数来告诉epoll要监控相应的文件描述符的什么事件:
1.struct
2.memset(&
3.eventItem.events
4.eventItem.data.fd
5.result
这里就是告诉mEpollFd,它要监控mWa
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Android 应用程序 消息 处理 机制 LooperHandler 分析
![提示](https://static.bingdoc.com/images/bang_tan.gif)