Android ANR 起因的探究

        一直以来只知道Android上ANR是因为主线程卡住了,一般5秒就会出现ANR的dialog,一直也没有去深究这个来源。最近在写一个测试程序的时候发现,在主线程上调用了一个阻塞的binder 调用,阻塞了很长时间居然没有ANR,很奇怪,所以终于下决心好好研究下这个问题。

        要搞清楚这个问题,所掌握的线索就只有ANR的时候所弹出的dialog,那么就从dialog上的字符串开始,还好这个“not responding”还是比较独特的。很容易就找到了AppNotResponding这个dilaog类,他是BaseErrorDialog类的子类,果然不出所料他是一个WindowManager.LayoutParams.TYPE_SYSTEM_ALERT的类,而且设置了很多的flag,保证它能显示在APP的前面,不去深究这个了,还是看看是谁让它显示的呢?

早ActivityManagerService里找到一段代码:

            case SHOW_NOT_RESPONDING_MSG: {
                synchronized (ActivityManagerService.this) {
                    HashMap data = (HashMap) msg.obj;
                    ProcessRecord proc = (ProcessRecord)data.get("app");
                    if (proc != null && proc.anrDialog != null) {
                        Slog.e(TAG, "App already has anr dialog: " + proc);
                        return;
                    }
                    
                    Intent intent = new Intent("android.intent.action.ANR");
                    if (!mProcessesReady) {
                        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
                                | Intent.FLAG_RECEIVER_FOREGROUND);
                    }
                    broadcastIntentLocked(null, null, intent,
                            null, null, 0, null, null, null, AppOpsManager.OP_NONE,
                            false, false, MY_PID, Process.SYSTEM_UID, 0 /* TODO: Verify */);

                    if (mShowDialogs) {
                        Dialog d = new AppNotRespondingDialog(ActivityManagerService.this,
                                mContext, proc, (ActivityRecord)data.get("activity"),
                                msg.arg1 != 0);
                        d.show();
                        proc.anrDialog = d;
                    } else {
                        // Just kill the app if there is no dialog to be shown.
                        killAppAtUsersRequest(proc, null);
                    }
                }
                
                ensureBootCompleted();
            } break;
可以发现有人给ActivityManagerService发了这个
SHOW_NOT_RESPONDING_MSG
的消息,才导致了这个可恶的dialog给show了出来,是谁,站出来,我保证不打死它。

在同一个文件里发现了

   final void appNotResponding(ProcessRecord app, ActivityRecord activity,
            ActivityRecord parent, boolean aboveSystem, final String annotation) {
        ArrayList<Integer> firstPids = new ArrayList<Integer>(5);
        SparseArray<Boolean> lastPids = new SparseArray<Boolean>(20);

            ......
    
            // Bring up the infamous App Not Responding dialog
            Message msg = Message.obtain();
            HashMap map = new HashMap();
            msg.what = <span style="color: rgb(255, 102, 102);">SHOW_NOT_RESPONDING_MSG</span>;
            msg.obj = map;
            msg.arg1 = aboveSystem ? 1 : 0;
            map.put("app", app);
            if (activity != null) {
                map.put("activity", activity);
            }
    
            mHandler.sendMessage(msg);
        }
    }
再继续往前追溯真凶 ,原来是InputMonitor搞得鬼。而InputMonitor是WindowManager放到InputManager中的回调。它有一个notifyANR的方法才是这个事件的源头。

    // Native callback.
    private long notifyANR(InputApplicationHandle inputApplicationHandle,
            InputWindowHandle inputWindowHandle) {
        return mWindowManagerCallbacks.notifyANR(inputApplicationHandle, inputWindowHandle);
    }
这是InputManagerService里面的代码,Native callback,看到这个注释会崩溃,有木有。

等一等,ANR和InputManagerService 扯上了,什么情况。看来不追到native的代码是不行了。

void InputDispatcher::doNotifyANRLockedInterruptible(
        CommandEntry* commandEntry) {
    mLock.unlock();

    nsecs_t newTimeout = mPolicy-><span style="color:#ff6666;">notifyANR</span>(
            commandEntry->inputApplicationHandle, commandEntry->inputWindowHandle,
            commandEntry->reason);

    mLock.lock();

    resumeAfterTargetsNotReadyTimeoutLocked(newTimeout,
            commandEntry->inputWindowHandle != NULL 
                    ? commandEntry->inputWindowHandle->getInputChannel() : NULL);
}
Native的InputDispatcher会报告这个情况。 在InputDispatcher::handleTargetsNotReadyLocked方法中有这么段代码:

 if (<span style="color:#ff6666;">currentTime >= mInputTargetWaitTimeoutTime</span>) {
        onANRLocked(currentTime, applicationHandle, windowHandle,
        ......
    }

mInputTargetWaitTimeoutTime = currentTime + timeout;
timeout =  DEFAULT_INPUT_DISPATCHING_TIMEOUT;
DEFAULT_INPUT_DISPATCHING_TIMEOUT 就是5秒。ok就是这个了。

再进一步查看是谁调用这个doNotifyANRLockedInterruptible, 最终可以发现是在:

bool InputDispatcherThread::threadLoop() {
    mDispatcher->dispatchOnce();
    return true;
}
这个loop中,这样就可以理解了,原来是在分发input事件 的时候超时了,才会出现这个一连串的反映,最终导致ANR 的dialog给show了出来。

再度感叹,好复杂。不过借此倒可以学习到InputManager - WindowManger -ActivityManager 三者的一定的概念。


  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值