Android 的Handler工作流程梳理

之前在子线程和主线程创建使用Handler不同处的源码分析时,追了一下应用创建和使用Handler的相关源码。发现了在线程中使用Handler,最后就会进入loop循环,子线程要手动退出,主线程是不死就不退出;同时也和另一个问题相遇了:那就是应用的主线程最后也都在loop里没出来【activityThread的Main方法最后执行了Looper.loop()】为什么不会导致应用发生ANR呢?.今天不对这具体深入;就主要对Handler的消息循环,消息发送和消息处理的流程进行分析.这块梳理完后,对不引发ANR的问题也就可以帮助理解了

消息循环

在loop循环中,会一直等待和处理消息。我们就从Looper.java的loop()方法入手:

/**
    * Run the message queue in this thread. Be sure to call
    * {@link #quit()} to end the loop.
    */
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;
 
    for (;;) {
        Message msg = queue.next(); // might block
        if (msg == null) {
            return;
        }
        ...
        try {
            msg.target.dispatchMessage(msg);
            dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
        } 
        ....
        msg.recycleUnchecked();
    }
}

在loop()中,先对其中的重要的变量进行说明:

me : looper对象,在Looper.java类的prepare()方法中创建,并保存在sMainLooper中,这里通过myLooper()方法获取到

queue:MessageQueue对象,在Looper类构造函数里创建的,所以Looper中就持有了messageQueue的对象。

msg: Message;

msg.target:这就是handler

         loop中有个for大循环,里面就在不断的获取和处理消息。在循环中,先使用MessageQueue的next()方法从MessageQueue中获取message,如果返回的消息为空,loop就会退出-->线程就会退出(要是在主线程中的话-->最后应用也会退出)。我这看到这里时就很疑惑了,难道一个应用在活着的时候,他的MessageQueue就不能为空?但是显然应用的MessageQueue是为有空的时候,那问题应该就出在messageQueue的next()方法里,或许这个next()里面有什么特殊,导致不会返回空。

       抱着问题跳转到MessageQueue.java中看看这个next()方法里面是怎么样做的:

Message next() {
    final long ptr = mPtr;
    ...
    int nextPollTimeoutMillis = 0;
    for (;;) {
       ...
        nativePollOnce(ptr, nextPollTimeoutMillis);
       ...
    }
}

       在nex()方法中也有个for循环,它里面有个nativePollOnce()调用。我们就看看Native层的实现,接下来跳转到Native层的android_os_MessageQueue.cpp【这是MessageQueue对应的Native文件】

static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,
        jlong ptr, jint timeoutMillis) {
    NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
    nativeMessageQueue->pollOnce(env, obj, timeoutMillis);
}

          在android_os_MessageQueue_nativePollOnc()函数里面,通过nativePollOnce传进来的ptr找到NativeMessageQueue指针。然后调用其pollOnce()函数:

void NativeMessageQueue::pollOnce(JNIEnv* env, jobject pollObj, int timeoutMillis) {
    ...
    mLooper->pollOnce(timeoutMillis);
    mPollObj = NULL;
    mPollEnv = NULL;
    ...
}

         在pollOnce()函数中这个mLooper就是Looper的一个指针,在android_os_MessageQueue.h中声明的,所以在Native层的MessageQueue持有Looper指针,【Java层是Looper中持有MessageQueue对象】接着是调用了Looper的pollOnce()方法:

         我们就跳转到Looper.cpp中看看Looper的pollOnce()是怎么做的:

int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
    int result = 0;
    for (;;) {
       ...
        result = pollInner(timeoutMillis);
    }
}

         在pollOnce()中的for循环中调用了pollInner();我们在跳转到pollInner()中看看:

int Looper::pollInner(int timeoutMillis) {
    ...
    struct epoll_event eventItems[EPOLL_MAX_EVENTS];
    int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
    ...
    if (eventCount < 0) {
        if (errno == EINTR) {
            goto Done;
        }
        ALOGW("Poll failed with an unexpected error: %s", strerror(errno));
        result = POLL_ERROR;
        goto Done;
    }
    if (eventCount == 0) {
        ...
        result = POLL_TIMEOUT;
        goto Done;
    }
    ...
     for (int i = 0; i < eventCount; i++) {
        int fd = eventItems[i].data.fd;
        uint32_t epollEvents = eventItems[i].events;
        if (fd == mWakeEventFd) {
            if (epollEvents & EPOLLIN) {
                awoken();
           ...
        }
    ...
    return result;
}

         在pollInner()中,使用epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);来检测mEpollFd所监控的fd是否有IO事件发生,超时时间为timeoutMillis[这也是随我们从Java层传下来的]:【关于epoll,我们在之前分析Android_input系统分析EventHub::getevents时也有涉及,具体使用可以参考参考】

         当mEpollFd所监控的文件描述符发生了要监控的IO事件后或者监控时间超时后,线程就从epoll_wait返回了,否则线程就会在epoll_wait函数中进入睡眠状态了。返回后如果eventCount等于0,说明等待超时。果eventCount小于0,说明有错误。当eventCount大于0时,说明有IO事件发生:接着我们看awoken():

void Looper::awoken() {
    uint64_t counter;
    TEMP_FAILURE_RETRY(read(mWakeEventFd, &counter, sizeof(uint64_t)));
}


awoken()就是将mWakeEventFd句柄中的数据读出。他的作用就是把mWakeEventFd里面原有的数据读走,以便下次再写入。

到这里,差不多就把消息循环大致流程梳理完了。我们可以发现,当MessageQueue为空时,也不会返回为空,而是线程进入休眠状态,在native层使用epoll来监听是否有新的消息从而唤醒线程进行消息获取,所以在没有手动退出和没错误发生的情况下loop()是一直没有退出,从而线程,进程都不会退出。

消息发送

消息的发送不论我们使用Handler的sendMessageXXX()还是post()方法,最后都调用了sendMessageDelayed()方法;我们接下来就从Handler.java 的sendMessageDelaye()方法出发看看消息发送的流程是怎么样的:

public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
    if (delayMillis < 0) {
        delayMillis = 0;
    }
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

在sendMessageDelaye()方法中右调用了sendMessageAtTime()方法,我们接着跳转到sendMessageAtTime()方法中看看:

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
    MessageQueue queue = mQueue;
    ...
    return enqueueMessage(queue, msg, uptimeMillis);
}
...
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

从以上代码可以看出,最后在Handler中调用了MessageQueue中的enqueueMessage()方法:


boolean enqueueMessage(Message msg, long when) {
    ...
    synchronized (this) {
        ...
        msg.markInUse();
        msg.when = when;
        Message p = mMessages;
        boolean needWake;
        if (p == null || when == 0 || when < p.when) {
            // New head, wake up the event queue if blocked.
            msg.next = p;
            mMessages = msg;
            needWake = mBlocked;
        } else {
            ...
            needWake = mBlocked && p.target == null && msg.isAsynchronous();
            Message prev;
            for (;;) {
                prev = p;
                p = p.next;
                if (p == null || when < p.when) {
                    break;
                }
                if (needWake && p.isAsynchronous()) {
                    needWake = false;
                }
            }
            msg.next = p; // invariant: p == prev.next
            prev.next = msg;
        }

        // We can assume mPtr != 0 because mQuitting is false.
        if (needWake) {
            nativeWake(mPtr);
        }
    }
    return true;
}

以上就是MessageQueue.java的enqueueMessage()方法,主要工作就是

  1. 将消息插入到消息队列中
  2. 如果线程阻塞,我们要去唤醒线程去读取和处理消息

消息插入到MessageQueue中时有两种情况:

  if (p == null || when == 0 || when < p.when) {
            // New head, wake up the event queue if blocked.
            msg.next = p;
            mMessages = msg;
            needWake = mBlocked;
}

         ①. 当时消息队列此时消息为空,如上代码所示:直接将我们的消息放进去就好,说明此时messageQueue是阻塞状态的,我们需要唤醒事件队列;

 Message prev;
 for (;;) {
   prev = p;
   p = p.next;
   if (p == null || when < p.when) {
       break;
   }
   if (needWake && p.isAsynchronous()) {
      needWake = false;
   }
   }
   msg.next = p; // invariant: p == prev.next
   prev.next = msg;

         ②.如果当前消息队列不为空,那我们就要把我们要发送的消息插入带消息队列中去,这需要根据消息的时间来决定的我们插入到消息队列中的位置,具体逻辑参看上梳代码。

        当消息队列为空此时线程就需要我们去唤醒,这里是使用了一个Native方法nativeWake:那我们就跳转到Native层看看是怎么唤醒线程的,接下来我们跳转到android_os_MessageQueue.cpp中查看一下:

static void android_os_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jlong ptr) {
    NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
    nativeMessageQueue->wake();
}
...
void NativeMessageQueue::wake() {
    mLooper->wake();
}
...
void Looper::wake() {
    ...
    uint64_t inc = 1;
    ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd, &inc, sizeof(uint64_t)));
    ...
}

如上所以:这和消息循环是调用差不多,在Native层的android_os_MessageQueue中,最终是调用了Looper的wake(),在wake()中向mWakeEventFd写入数据,这个数据具体是什么不重要,主要当有数据写入;另一边的epoll_wait()就可以检测到有新的IO事件发生,从而唤醒线程来获取和处理消息。

消息处理

那接下来,消息队列理里有消息了,我们就需要几及时去处理消息;我们继续回到Looper.java中的loop()方法中,看到有下面这段代码:

    for (;;) {
        Message msg = queue.next(); // might block
        if (msg == null) {
            return;
        }
        ...
        try {
            msg.target.dispatchMessage(msg);
            dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
        } 

在for循环中不断地获取消息队列中的消息,【之前说过,当没有消息时,线程就会阻塞,所以这里的msg正常情况是不为空的】

这里是调用msg.target 的dispatchMessage()方法去处理消息;【之前说过,msg.target其实就是handler】.接下里我们看看Handler.java中是怎么处理的:

public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}
...

/**
* Subclasses must implement this to receive messages.
*/
public void handleMessage(Message msg) {
}

如上所示:如果msg 的callback不为空,就执行handleCallback()方法,否则如果mCallback不为空,就执行mCallbackd的handleMessage()方法;否则执行handleMessage()方法,一般情况是默认执行handleMessage()方法来处理消息.所以Handler 的子类必须要实现ahndleMessage()方法。对接收到的消息进行处理。

发布了41 篇原创文章 · 获赞 35 · 访问量 4312

猜你喜欢

转载自blog.csdn.net/weixin_38140931/article/details/102822065