其实对于初学者来说Handler的使用很容易会很容易上手,但是对于其中的机制的理解,却不会那么简单,甚至1,2年经验的android开发也可能对其内部实现原理不是很了解。
其中我经过的认知的过程
初识Handler,认为没必要,直接执行方法不就好了么,为什么还要post出去,然后再在handleMessage中作处理,多此一举。
异步可使用Handler,在主线程更新ui
主线程中有个Looper不断循环,从MessageQueue中获取到Message,而Handler只是往MessageQueue中塞入Message
非主线程中也可以创建自己的Looper来处理消息,不过要自己调用Looper.prepare(),Looper.loop(),初始化和开始轮询。
MessageQueue调用了jni层的方法来实现消息处理机制
笔者对整个android的消息处理机制的认知过程如上,经过的时间也很长,不是一蹴而就的。
今天就通过对其底层源码分析,和大家分享其内部实现原理,主要是第5步的认知,涉及到了jni层。这里主要介绍主线程中的Looper。
注意:android源码2.3.1
我们从头开始
frameworks/base/core/java/android/app/ActivityThread
...
...
public static final void main(String[] args) {
...
Looper.prepareMainLooper();
if (sMainThreadHandler == null) {
sMainThreadHandler = new Handler();
}
...
...
Looper.loop();
...
}
我们这里以ActivityThread为入口来分析消息处理机制,每个app启动的时候,都会创建自己的ActivityThread,并且在main方法中也会创建主线程中的Looper。
这里如果对app整个启动流程不是特别清楚的话,建议先看下
http://www.jianshu.com/p/9da5bb46835c
frameworks/base/core/java/android/os/Looper
...
private Looper() {
mQueue = new MessageQueue();
mRun = true;
mThread = Thread.currentThread();
}
...
public static final void prepare() {
...
sThreadLocal.set(new Looper());
}
public static final void prepareMainLooper() {
prepare();
setMainLooper(myLooper());
...
}
...
...
public static final Looper myLooper() {
return (Looper)sThreadLocal.get();
}
...
首先我们要理解ThreadLocal
这个类的作用
http://www.cnblogs.com/alphablox/archive/2013/01/20/2869061.html
简单来说,就是为了实现每个线程中有对应一个Looper。
上面代码主要做了
- 创建Looper,MessageQueue
- 把Looper与当前主线程关联
mQueue其实就是MessageQueue,在Looper的构造方法中创建。
下面分析下MessageQueue。
frameworks/base/core/java/android/os/MessageQueue
...
private native void nativeInit();
...
MessageQueue() {
nativeInit();
}
...
创建MessageQueue,其实主要调用了native方法。
其实在我们分析android源码的时候,要找到的对应的native方法,其实是一件很麻烦的事情,因为我们只知道方法名,并不知道对应的.c文件或者.cpp文件是哪个,这样就造成了我们阅读的障碍,这里有个比较笨的方法就是使用grep '**' -R .
来查询文件内容。但是毕竟android源码比较庞大,这样做其实还是很麻烦的,我这里暂时也没有更好的办法,主要还是参考了别人的文章,找到了对应的文件。
frameworks/base/core/jni/android_os_MessageQueue
...
NativeMessageQueue::NativeMessageQueue() {
mLooper = Looper::getForThread();
if (mLooper == NULL) {
mLooper = new Looper(false);
Looper::setForThread(mLooper);
}
}
...
static void android_os_MessageQueue_setNativeMessageQueue(JNIEnv* env, jobject messageQueueObj,
NativeMessageQueue* nativeMessageQueue) {
env->SetIntField(messageQueueObj, gMessageQueueClassInfo.mPtr,
reinterpret_cast<jint>(nativeMessageQueue));
}
...
static void android_os_MessageQueue_nativeInit(JNIEnv* env, jobject obj) {
NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
...
android_os_MessageQueue_setNativeMessageQueue(env, obj, nativeMessageQueue);
}
...
static JNINativeMethod gMessageQueueMethods[] = {
{ "nativeInit", "()V", (void*)android_os_MessageQueue_nativeInit },
...
};
...
简单地介绍下上面代码
- 注册对应的jni方法,如nativeInit.
- java层调用nativeInit后创建,NativeMessageQueue
- NativeMessageQueue中创建了Looper,如java层也是跟线程绑定,但是这个jni层的Looper,跟java层的 Looper没有关系。
- 利用JNIEnv方法给java 层中的MessageQueue对象的mPtr变量设置为NativeMessageQueue的地址。
下面是对jni层Looper的解析
frameworks/base/libs/utils/Looper
Looper::Looper(bool allowNonCallbacks) :
mAllowNonCallbacks(allowNonCallbacks),
mResponseIndex(0) {
...
int result = pipe(wakeFds);
...
mWakeReadPipeFd = wakeFds[0];
mWakeWritePipeFd = wakeFds[1];
...
mEpollFd = epoll_create(EPOLL_SIZE_HINT);
...
}
其实Looper的代码很多,这里我就贴出了关键的几行,但是涉及到的知识点却很多。
- pipe,创建读写管道
http://www.cnblogs.com/kunhu/p/3608109.html - epoll_create,管理io
http://www.cnblogs.com/Anker/archive/2013/08/17/3263780.html
其实我对这2者也并不是很熟悉。但是我知道以下几点,我认为就足够了。
- pipe创建读写2个通道
- epoll监听io
- epoll_wait会io阻塞,等到pipe管道中写入内容后,阻塞结束。
以上其实都是ActivityThread中调用Looper.prepareMainLooper之后的流程。主要是进行一些初始化,创建一些基础的对象。
下面我们来分析Looper.loop(),消息轮询。
frameworks/base/core/java/android/os/Looper
...
public static final void loop() {
Looper me = myLooper();
MessageQueue queue = me.mQueue;
while (true) {
Message msg = queue.next(); // might block
...
msg.target.dispatchMessage(msg);
...
msg.recycle();
}
}
}
...
这段代码就是开启while死循环,不断从MessageQueue中获取Message,一看最重要的肯定是queue.next()获取Message这个方法。
继续跟入。
frameworks/base/core/java/android/os/MessageQueue
...
final Message next() {
...
for (;;) {
...
nativePollOnce(mPtr, nextPollTimeoutMillis);
...
}
...
}
...
next方法去获取Message,最终还是调用nativePollOnce方法,传入的2个参数,这里介绍下。
mPtr,是MessageQueue的一个变量,在前面其实已经介绍过了,其实是指向jni层的NativeMessageQueue的地址。
nextPollTimeoutMillis其实是epoll_wait时,如果pipe中没有内容写入的等待时间,举个例子,如果nextPollTimeoutMillis为1000,那么这1秒中如果pipe中一直没有东西写入,那么,epoll_wait那么会阻塞1秒,然后继续运行,如果为0的话,epoll_wait就不会阻塞,如果为-1的话,如果pipe中没有东西写入,就会一直阻塞。
下面我们来看下nativePollOnce的具体实现。
frameworks/base/core/jni/android_os_MessageQueue
...
void NativeMessageQueue::pollOnce(int timeoutMillis) {
mLooper->pollOnce(timeoutMillis);
}
...
static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,
jint ptr, jint timeoutMillis) {
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
nativeMessageQueue->pollOnce(timeoutMillis);
}
...
其实在前面那一步就已经介绍了,ptr是NativeMessageQueue指针,在方法中强转为NativeMessageQueue对象后,调用他的pollOnce,
然后再调用NativeMessageQueue中的Looper的pollOnce方法
frameworks/base/libs/utils/Looper
...
int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
int result = 0;
for (;;) {
while (mResponseIndex < mResponses.size()) {
...
result = pollInner(timeoutMillis);
}
}
int Looper::pollInner(int timeoutMillis) {
...
...
int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
...
for (int i = 0; i < eventCount; i++) {
int fd = eventItems[i].data.fd;
uint32_t epollEvents = eventItems[i].events;
if (fd == mWakeReadPipeFd) {
if (epollEvents & EPOLLIN) {
awoken();
}
...
}
...
}
..
}
...
void Looper::awoken() {
...
do {
nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer));
} while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer));
}
...
- epoll_wait阻塞,等待
timeoutMillis
毫秒或者pipe中有输入 - 如果有消息过来了,其实会先往pipe中输入,解除epoll_wait阻塞
- for循环eventCount,判断是否是当前线程的管道的fd,如果是,就调用awoken方法,read一下把东西读出来
其实到这里了,已经把大部分重要的代码贴出,下面我们来过一遍,走下整个流程,来梳理一下。
- Looper初始化,这里就不过多介绍了。
- 调用loop,不断从MessageQueue取数据。
第一次调用时,nextPollTimeoutMillis=0
,epoll_wait没有造成阻塞,马上运行到下面,由于刚开始,还没有消息过来eventCount=0
,所以
java层中。
frameworks/base/core/java/android/os/MessageQueue
...
final Message next() {
...
for (;;) {
...
nativePollOnce(mPtr, nextPollTimeoutMillis);
synchronized (this) {
...
final Message msg = mMessages;
if (msg != null) {
final long when = msg.when;
if (now >= when) {
mBlocked = false;
mMessages = msg.next;
msg.next = null;
if (Config.LOGV) Log.v("MessageQueue", "Returning message: " + msg);
return msg;
else{
nextPollTimeoutMillis = -1;
}
...
}
}
...
调用nativePollOnce
其实主要作用就是在jni层中epoll_wait等待pipe管道有新的输入,即有新的 Message。
刚刚开始,没有阻塞,所以nativePollOnce
马上返回。
Message
其实是一个链表结构。
mMessages变量始终指向第一个Message.
由于刚开始还没有Message,所有mMessages = null
进入else,设置nextPollTimeoutMillis = -1;
由于还处于for之中,所以继续调用native方法nativePollOnce
,
前面我已经介绍了,当nextPollTimeoutMillis=-1时,其实在epoll_wait会一直阻塞。
这时,我又不得不提到这篇优秀的文章
http://www.jianshu.com/p/9da5bb46835c
这里我并不清楚第一个Message消息是哪里传过来的,但是在App启动时Activity的生命周期中大部分都是利用handleMessage来处理的,姑且当作是第一个Message吧。
我们都知道是通过Handler来发送Message,由于Handler中发送 Message方法很多,但是最后都是调用sendMessageAtTime
frameworks/base/core/java/android/os/Handler
...
public boolean sendMessageAtTime(Message msg, long uptimeMillis)
{
boolean sent = false;
MessageQueue queue = mQueue;
if (queue != null) {
msg.target = this;
sent = queue.enqueueMessage(msg, uptimeMillis);
}
else {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
}
return sent;
}
...
这里的mQueue其实就是我们在Looper中创建的MessageQueue.
frameworks/base/core/java/android/os/MessageQueue
...
final boolean enqueueMessage(Message msg, long when) {
...
final boolean needWake;
synchronized (this) {
...
Message p = mMessages;
if (p == null || when == 0 || when < p.when) {
msg.next = p;
mMessages = msg;
needWake = mBlocked; // new head, might need to wake up
} else {
Message prev = null;
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
msg.next = prev.next;
prev.next = msg;
needWake = false; // still waiting on head, no need to wake up
}
}
if (needWake) {
nativeWake(mPtr);
}
return true;
}
...
enqueueMessage其实这个方法很重要,它对Message先进行了排序,要早运行的排在了前面,比较晚运行的排在队列后面。
前面介绍了mMessages其实是指向Message链表的第一个Message.
这时候来了个新的Message,由于是刚刚开始mMessage = null;
所以进入了if()中的语句。
这里有个变量需要特别解释,needWake
,顾名思义,是否需要唤醒。
就整篇文章而言,其实阻塞的就一个地方,就是epoll_wait,所以needWake指的需不需要唤醒,也就是指的这里。
mBlocked代表,当前的epoll_wait是否正在阻塞,如果是阻塞的,
mBlocked = true,所以needWake = mBlocked = true,也就是需要唤醒。
如果非阻塞那么,needWake = false.
最终是
if (needWake) {
nativeWake(mPtr);
}
前面已经介绍了,由于nextPollTimeoutMillis=-1,epoll_wait正在阻塞,所以needWake = true;所以这个时候需要唤醒。
这里我就 不介绍跳转过程了,最终调用jni层的Looper中的wake方法
frameworks/base/libs/utils/Looper
...
void Looper::wake() {
...
do {
nWrite = write(mWakeWritePipeFd, "W", 1);
} while (nWrite == -1 && errno == EINTR);
...
}
...
前面其实我已经介绍过了epoll_wait有2种方式不再阻塞
- 等待传入的timeout参数时间到
- pipe管道中有新的输入
由于这个时候的timeout=-1代表一直阻塞,所以只能通过往pipe管道中写入东西,才能不阻塞epoll_wait.如上write一个内容。其实写的内容是什么并不重要,如上,其实就是写了一个W
字符。
这个时候,epoll_wait不再阻塞,继续往下运行。
frameworks/base/libs/utils/Looper
...
int Looper::pollInner(int timeoutMillis) {
...
int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
...
for (int i = 0; i < eventCount; i++) {
int fd = eventItems[i].data.fd;
uint32_t epollEvents = eventItems[i].events;
if (fd == mWakeReadPipeFd) {
if (epollEvents & EPOLLIN) {
awoken();
}
...
}
...
...
}
...
void Looper::awoken() {
...
do {
nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer));
} while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer));
}
...
这个时候进入awoken方法,把写入内容读出。没有造成阻塞。
也就是java层中的MessageQueue中的next方法中的nativePollOnce
没有造成阻塞,继续运行。
frameworks/base/core/java/android/os/MessageQueue
...
final Message next() {
...
for (;;) {
...
nativePollOnce(mPtr, nextPollTimeoutMillis);
...
final Message msg = mMessages;
if (msg != null) {
final long when = msg.when;
if (now >= when) {
mBlocked = false;
mMessages = msg.next;
msg.next = null;
if (Config.LOGV) Log.v("MessageQueue", "Returning message: " + msg);
return msg;
} else {
nextPollTimeoutMillis = (int) Math.min(when - now, Integer.MAX_VALUE);
}
} else {
nextPollTimeoutMillis = -1;
}
...
}
...
}
...
这时nativePollOnce
阻塞结束,继续运行,if (now >= when) 是为了判断这个消息是否延迟,对应我们Handler中延迟发送的一些方法,如postDelay()等。这里直接认为没有延时。
在Message链表中把第一个Message移除返回。
最后调用Handler的handleMessage,这不是重点我就直接略过了。
等到Message都处理完了之后,获取到Message 为null。nextPollTimeoutMillis又被置为-1,然后又在epoll_wait中进行阻塞。
这里对延时发送Message的没有详细讲解,主要还是2点。
- 在添加Message的时候进行时间排序了,把Message插入到对应的位置。
- 主要还是利用nextPollTimeoutMillis,在epoll_wait的时候阻塞,等待。
这里我就不再详细赘述了。
问题
在分析源码的时候,我遇到了一个问题,整个消息机制,其实就是一个死循环,一直在执行,当没有消息的时候epoll_wait阻塞。
为什么epoll_wait在主线程阻塞不会造成卡顿,或者ANR?
我这里就不再过多赘述了。