【Android】在主线程中的Looper会不会导致ANR?

360 阅读3分钟

深入Android的学习,肯定会接触到Android的消息机制,对Android消息机制的全盘了解会更容易回答标题的疑问。

Looper.loop()

著名的ActivityThread.main()方法中,倒数第二行代码就是Looper.loop(),Looper.loop()开启了消息循环,深入进去看看代码:

public static void loop() {
        //注释1
        final Looper me = myLooper();
        ... ...
        //注释2
        final MessageQueue queue = me.mQueue;
        ... ...
        //注释3
        for (;;) {
            ... ...
        }
    }

注释1:获取Looper对象---想要深入了解这一部分,需要深入学习一下ThreadLocal的实现机制;

注释2:从Looper中获取消息队列;

注释3:开启消息循环,就是我们常说的死循环。

阻塞、唤醒

      for (;;) {
            Message msg = queue.next(); // might block

从代码来看,死循环第一行调用了消息队列的next()方法,并且注释说“might block”,可能会阻塞,这里需要深入看下这个next()方法---MessageQueue.next()

  Message next() {
        ... ...
        int nextPollTimeoutMillis = 0;
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }
            //注释1
            nativePollOnce(ptr, nextPollTimeoutMillis);

注释1:在该方法中,有一开始出现了一行native方法 , 当没有消息入队的时候,nativePollOnce会阻塞在这里。

这里继续解释一下,nextPollTimeoutMillis如果为0表示阻塞,如果nextPollTimeoutMillis=-1会一直阻塞,一直阻塞到被唤醒。

什么时候被唤醒?我们知道在消息机制中,如果有消息入队就会处理消息,那深入到入队看看,MessageQueue.enqueueMessage()

 boolean enqueueMessage(Message msg, long when) {
        ... ...
            // We can assume mPtr != 0 because mQuitting is false.
           
            if (needWake) {
                //注释1
                nativeWake(mPtr);
            }
        }
        return true;
    }

注释1:从字面意思很容易看出这里是唤醒的意思。(老外的代码很容易被理解,这也是9.0就算很多被禁了,也能很容易从c++层面看到哪里可以入手,哈哈~~)

知道了阻塞和唤醒的具体调用位置,也需要知道在native层面如何实现,这里的就进一步讲到epoll机制。大概的意思就是在epool_wait时一直阻塞着会释放CPU的占用,这也是为什么这个死循环不占用高CPU的原因。(PS:androidxref.com 在先阅读源码,赶快行动起来)

另外,消息机制中还有一个延时入队的实现,也需要了解一下。

ANR相关

先看一段代码,在ProcessRecord.appNotResponding大概最后的位置

if (mService.mUiHandler != null) {
        // Bring up the infamous App Not Responding dialog
        Message msg = Message.obtain();
        msg.what = ActivityManagerService.SHOW_NOT_RESPONDING_UI_MSG;
        msg.obj = new AppNotRespondingDialog.Data(this, aInfo, aboveSystem);
        mService.mUiHandler.sendMessage(msg);
     }

从官方给出的注释来看可以App Not Responding 也就是我们常说的ANR的来源。

几年前面试Android岗位有时候也会问,有哪些ANR,分别对应的时间是多少?(那是多美好的时代!!)

这里看看源码来找到对应的AtivityManagerService中代码片段

    // How long we allow a receiver to run before giving up on it.
    static final int BROADCAST_FG_TIMEOUT = 10*1000;
    static final int BROADCAST_BG_TIMEOUT = 60*1000;
    

ActiveServices.java

    // How long we wait for a service to finish executing.
    static final int SERVICE_TIMEOUT = 20*1000;

    // How long we wait for a service to finish executing.
    static final int SERVICE_BACKGROUND_TIMEOUT = SERVICE_TIMEOUT * 10;

    // How long the startForegroundService() grace period is to get around to
    // calling startForeground() before we ANR + stop it.
    static final int SERVICE_START_FOREGROUND_TIMEOUT = 10*1000;

当然还有ContentPorvider等设置,所以看了源码这些内容也就很清晰的得到了答案。

关于ANR 我们还需要了解如何查找、如何读取等 ,也需要总结ANR的产生原因。

总结

从上面的分析来看,Looper的死循环是一种消息机制,ANR是整个Android大体系下的一个小模块的消息处理的响应方式。两者并没有什么关系,所以在主线程中的Looper会不会导致ANR。