如下采用Android源码的android-11.0.0_r48分支进行,不同版本源码差异巨大。
ANR的捕获起点为ProcessRecord.appNotResponding方法,本文由appNotResponding逆推ANR的产生机制。
源码:frameworks/base/services/core/java/com/android/server/am/ProcessRecord.java。
通过源码搜索appNotResponding,发现系统提供了AnrHelper类,封装了ProcessRecord.appNotResponding,所有ANR产生后,调用都会走到这里。通过搜索发现Activity、Broadcast、Service、ContentProvider都会调用AnrHelper.appNotResponding,也就是说Android四大组件都有可能产生ANR。
源码:frameworks/base/services/core/java/com/android/server/am/AnrHelper.java
在开始Activity ANR之前,先问个问题:
创建一个Android Hello World工程,添加一个Button,在Button的onClick中回调SystemClock.sleep(10 * 1000)。
从AnrHelper.appNotResponding出发,搜索调用地方。发现在ActivityManagerService中有inputDispatchingTimedOut方法,最终调用到AnrHelper.appNotResponding,最终走到ANR流程。此处通过函数名基本得到是输入超时导致的ANR。
源码:frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
通过查找inputDispatchingTimedOut调用,最终发现ActivityRecord、AnrController两个类中有调用
源码:frameworks/base/services/core/java/com/android/server/wm/ActivityRecord.java
源码:frameworks/base/services/core/java/com/android/server/wm/AnrController.java
此处通过路径发现,Activity过来的ANR的触发点在WindowManager中,也就是Activity中的Window才会触发ANR。
继续看源码ActivityRecord中的inputDispatchingTimedOut,发现其只有转调WindowManager.inputDispatchingTimedOut,并没有触发逻辑。
继续看源码AnrController中的inputDispatchingTimedOut,发现其中提供了几种类型的ANR函数供调用:notifyAppUnresponsive、notifyWindowUnresponsive、notifyGestureMonitorUnresponsive,如下逐个分析。
frameworks/base/services/core/java/com/android/server/wm/InputManagerCallback.java (notifyNoFocusedWindowAnr)
frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp (NativeInputManager::notifyNoFocusedWindowAnr)
frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp (InputDispatcher::doNotifyNoFocusedWindowAnrLockedInterruptible)
frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp (InputDispatcher::onAnrLocked(std::shared_ptr application))
frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp (InputDispatcher::processNoFocusedWindowAnrLocked)
frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp (InputDispatcher::processAnrsLocked)
frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp (InputDispatcher::dispatchOnce)
frameworks/base/services/core/java/com/android/server/wm/InputManagerCallback.java (notifyWindowUnresponsive)
frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp (NativeInputManager::notifyWindowUnresponsive)
frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp (InputDispatcher::doNotifyWindowUnresponsiveLockedInterruptible)
frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp (InputDispatcher::sendWindowUnresponsiveCommandLocked)
frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp (InputDispatcher::processConnectionUnresponsiveLocked)
frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp (InputDispatcher::onAnrLocked(const sp& connection))
frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp (InputDispatcher::processAnrsLocked)
frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp (InputDispatcher::dispatchOnce)
frameworks/base/services/core/java/com/android/server/wm/InputManagerCallback.java (notifyGestureMonitorUnresponsive)
frameworks/base/services/core/java/com/android/server/input/InputManagerService.java (notifyMonitorUnresponsive)
frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp (NativeInputManager::notifyMonitorUnresponsive)
frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp (InputDispatcher::doNotifyMonitorUnresponsiveLockedInterruptible)
frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp (InputDispatcher::sendMonitorUnresponsiveCommandLocked)
frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp (InputDispatcher::processConnectionUnresponsiveLocked)
frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp (InputDispatcher::onAnrLocked(const sp& connection))
frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp (InputDispatcher::processAnrsLocked)
frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp (InputDispatcher::dispatchOnce)
从调用栈可见,如上3种类型的ANR最终都会收敛至InputDispatcher::dispatchOnce函数,那么接下来先看此函数。
InputDispatcher::dispatchOnce为InputDispatcher::start函数中开启的名为InputDispatcher线程的运行体,InputDispatcher线程启动后,如果没有停止,则一直循环调用InputDispatcher::dispatchOnce,此函数最后的mLooper->pollOnce(timeoutMillis)将线程Block住,等待timeoutMillis时间,然后开启下一轮InputDispatcher::dispatchOnce调用(mLooper采用Linux epoll机制进行block,等待timeoutMillis的调用为epoll_wait(…, timeoutMillis)),timeoutMillis的值为5000,也即每5秒。
在某些事件发生时(如:输入事件),可以调用mLooper->wake(),停掉等待,立刻调用InputDispatcher::dispatchOnce,然后其中会调用processAnrsLocked判断此次是否发生ANR。
接下来看InputDispatcher::processAnrsLocked,此函数判断是否产生ANR,函数的注释简单来说就是“等待的事件时长超过Window timeout(5秒),就产生ANR”,接下来通过源码再行验证一下。
再回到最开始的InputDispatcher::dispatchOnce函数,除了轮询调用外,在某些事件发生时(调用mLooper->wake()停掉等待),也会被调用。那么也就意味着每个促使mLooper->wake()被调用的事件,都将可能产生ANR。
源码:frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
在源码中搜索mLooper->wake(),发现有22个匹配,源码中几乎每个调用都有注释,如下列举几个常见的事件:
回到上面3个ANR函数调用(notifyAppUnresponsive、notifyWindowUnresponsive、notifyGestureMonitorUnresponsive),这3个函数分别代表3种类型的ANR。
注:经测试,华为/小米厂商将ANR的5000ms改为了8000ms。可以通过adb shell dumpsys input | grep dispatchingTimeout来获取系统值。
adb logcat -s ActivityManager可以查看ANR的初步信息,如:
E ActivityManager: ANR in com.huya.mtp.myanr (com.huya.mtp.myanr/.MainActivity)
E ActivityManager: PID: 10789
E ActivityManager: Reason: Input dispatching timed out (f69705 com.huya.mtp.myanr/com.huya.mtp.myanr.MainActivity (server) is not responding. Waited 5005ms for MotionEvent(deviceId=9, source=0x00005002, displayId=0, action=DOWN, actionButton=0x00000000, flags=0x00000000, metaState=0x00000000, buttonState=0x00000000, classification=NONE, edgeFlags=0x00000000, xPrecision=30.3, yPrecision=17.1, xCursorPosition=nan, yCursorPosition=nan, pointers=[0: (405.0, 269.9)]), policyFlags=0x62000000)
E ActivityManager: Parent: com.huya.mtp.myanr/.MainActivity
在开始Broadcast ANR之前,同样问个问题:
创建一个Android Hello World工程,动态注册一个BroadcastReceiver,在onReceiver中调用SystemClock.sleep(100 * 1000),添加一个Button,点击后发送广播让动态注册的BroadcastReceiver接收。
源码:frameworks/base/services/core/java/com/android/server/am/BroadcastQueue.java
源码:frameworks/base/services/core/java/com/android/server/am/BroadcastQueue/BroadcastDispatcher.java
源码:frameworks/base/services/core/java/com/android/server/am/BroadcastQueue.java
Context.sendBroadcast --> ContextWrapper.sendBroadcast --> ContextImpl.sendBroadcast --> ActivityManagerService.broadcastIntentWithFeature
Context.sendOrderedBroadcast --> ContextWrapper.sendOrderedBroadcast --> ContextImpl.sendOrderedBroadcast --> ActivityManagerService.broadcastIntentWithFeature
调用最终通过Binder跨进程走到了ActivityManagerService.broadcastIntentWithFeature,通过参数设置了是否有序,参数为broadcastIntentWithFeature方法中的倒数第3个参数(boolean serialized),broadcastIntentWithFeature方法有15个参数。
ActivityManagerService.broadcastIntentWithFeature ----> ActivityManagerService.broadcastIntentLocked
经过重载调用来到了ActivityManagerService.broadcastIntentLocked方法,这又是一个700行左右的大方法,看来接下来只能死磕了。此处核心关注点在于广播如何被保存起来。
6.1. 函数最开始判断是否为Instant状态、判断广播白名单、判断系统是否完成启动、判断UserId等,这些跟并行与串行广播没啥关系,跳过
6.2. 判断bOptions是否为null(注意:不是变量brOptions),bOptions是给System发送广播用的,App发送的广播bOptions写死为null,所以跳过bOptions判断的整段代码。
6.3. 判断是否为Protected Broadcast,根据注释,这仍然是System行为,跳过
6.4. 接下来对发送者与系统广播做一次安全检测,不符合则抛出SecurityException,继续跟并行与串行没啥关系,跳过
6.5. 如果广播的action不为null,则进入一个巨大的switch case,用于处理Intent.ACTION_PACKAGE_REMOVED等预定义广播,跟我们自定义的没关系,继续跳过
6.6. 接下来处理粘性广播(sticky),并为之添加一些属性。由于粘性不在本文范围内,并且也已经废弃,跳过
6.7. 接下来获取广播的users,跳过
6.8. 接下来看到注释“// Figure out who all will receive this broadcast”,来了来了,读完500行代码后,终于来到广播接收的处理逻辑处了。
6.9. 首先判断Intent是否不具备Intent.FLAG_RECEIVER_REGISTERED_ONLY属性,如果不具备则调用collectReceiverComponents收集receivers。根据Android文档Intent.FLAG_RECEIVER_REGISTERED_ONLY表示动态注册的广播类型,不会Launch BroadcastReceiver components。
6.9.1. 接下去开启支线任务,进到collectReceiverComponents查看收集到了一些什么receivers,方法返回类型为List,ResolveInfo即为IntentFilter中的Intent。
6.9.2. collectReceiverComponents的主要是调用了AppGlobals.getPackageManager().queryIntentReceivers(intent, resolvedType, pmFlags, user).getList()来收集List
6.9.2.1. 接下来继续开启支线任务,进入到queryIntentReceivers看看怎么样将List收集到的。
PackageManagerService.queryIntentReceivers --> PackageManagerService.queryIntentReceiversInternal --> ComponentResolver.queryReceivers --> ComponentResolver.queryIntent
ComponentResolver类用于解析Android四大组件,最终的调用ComponentResolver.queryIntent将所有的静态与动态注册广播的Intent的query出来,并调用PackageManagerService.applyPostResolutionFilter解析后保存到List中,最终返回给collectReceiverComponents(这个支线扯远了,已经到PackageManagerService中了)。
6.10. 回到ActivityManagerService,此时receivers已经赋值,其中的内容为所有通过Resolve的Intent的List,也就是所有静态与动态注册的广播接收者List。
6.11. 接下来调用mReceiverResolver.queryIntent为registeredReceivers赋值。好家伙,又来一个广播接收者List,此处支线下去看的话,发现返回的仅为动态注册的接收者List(支线任务就不展开讲了,感兴趣可以自己阅读源码)。
6.12. 接下来采用白名单对registeredReceivers进行处理,跟并行与串行无关,跳过。
6.13. 此处总结一下,上面分析了那么多,最终得到了两个广播接收者List:
6.14. 接下来判断:如果不是串行广播,并且具备接收者(registeredReceivers.size() > 0),则调用queue.enqueueParallelBroadcastLocked发送广播。从方法名可以看出来,此处是并行的,enqueueParallelBroadcastLocked将广播添加到mParallelBroadcasts,挥应了上面逆推中的第3点,而mParallelBroadcasts中的广播怎么被处理,后面再讲。
6.15. 接下来判断是否存在receivers,如果存在,则进行安全检测,避免监听Intent.ACTION_PACKAGE_ADDED做App立即启动的后门,此处与我们主题无关,跳过。
6.16. 接下来按照广播的优先级priority进行排序,串行的广播按照优先级发送。
6.17. 接下来做一些List操作后,最终调用queue.enqueueOrderedBroadcastLocked发送广播。同样从方法名可以看出,此处是串行的,enqueueOrderedBroadcastLocked将广播添加到mDispatcher中,挥应了上面逆推中的第2点,至于广播怎么被处理,同样后面再讲。
6.18. 看完这个700行的大方法后,最后总结一下:
7.1. 在queue.enqueueParallelBroadcastLocked与queue.enqueueOrderedBroadcastLocked方法后,都能看到queue.scheduleBroadcastsLocked调用,从函数名看出这个应该是调度广播去消费了。
7.2. 进入scheduleBroadcastsLocked方法,其中采用Handler发送了BROADCAST_INTENT_MSG消息。
7.3. 在接受BROADCAST_INTENT_MSG Handler消息的之处,看到调用processNextBroadcast,也就是处理下一条广播。
7.4. 进入processNextBroadcast后看到其同步调用processNextBroadcastLocked,而processNextBroadcastLocked便是广播消费的主体了。
源码:frameworks/base/services/core/java/com/android/server/am/BroadcastQueue.java
8.1. 方法开始定义一个变量BroadcastRecord r,这便是广播的记录,代表着当前广播,也就是整个方法都围绕着这个r进行。
8.2. 接下里更新CPU等内容,跳过。
8.3. 接下来看到一个while循环(while (mParallelBroadcasts.size() > 0) {),mParallelBroadcasts是并行队列,所以接下来这个循环便是处理并行广播的。这个循环的核心调用为deliverToRegisteredReceiverLocked,将广播deliver至RegisteredReceiver类型的的Receiver中,根据第6点知识,RegisteredReceiver便是动态注册的广播列表。
8.3.1. 接下来又该开启支线任务了。deliverToRegisteredReceiverLocked又是一个200多行的大方法,继续死磕
8.3.2. 方法开始就定义一个boolean skip = false,表示是否要跳过这个广播的deliver。
8.3.3. 接下来大约150行都是各种if判断,if判断为真,则skip = true,if中判断各种权限之类的。如果最终skip = true,则跳过这次deliver,此处忽略这种被skip的情况,核心关注如何被发送。
8.3.4. 接下来继续判断权限,然后继续跳过deliver,跳过。
8.3.5. 如果是ordered的广播,还要处理oomAdj,跟如何发送也无关,继续跳过。
8.3.6. 接下来是一个try catch。看到if判断,一般来说filter.receiverList.app.inFullBackup为false(inFullBackup:Process is currently hosting a backup agent for backup or restore),即会走到else处,最终调用核心函数performReceiveLocked,开始接受广播。
8.3.6.1. 进入performReceiveLocked方法,根据不同条件,可能会转调scheduleRegisteredReceiver或performReceive。
8.3.6.2. scheduleRegisteredReceiver的定义在IApplicationThread.aidl中,此aidl有oneway标记,即异步执行。
源码:frameworks/base/core/java/android/app/IApplicationThread.aidl
8.3.6.3. performReceive的定义在IIntentReceiver.aidl中,同样,此aidl也有oneway标记,同样是异步执行。
源码:frameworks/base/core/java/android/content/IIntentReceiver.aidl
8.3.7. 从performReceiveLocked函数中出来后,回到processNextBroadcastLocked函数的while循环中,由于异步执行,所以while循环会很快发送完所有消息,让registeredReceivers分别去处理,所以这也就是广播的并行发送逻辑。
8.4. 接下去该到串行队列mDispatcher的处理了(由于一个广播可以被某应用动态注册为并行广播,也可以被其他应用静态注册为串行广播,所以接下去还需要处理串行队列)。接着往下看processNextBroadcastLocked方法。代码中也可看到注释:// Now take care of the next serialized one…。
8.5. 首先处理进程等待的特殊逻辑,此处不关心,跳过。
8.6. 接下去看到一个有着200行代码的巨大的do while循环,首先调用r = mDispatcher.getNextBroadcastLocked(now)去取串行队列中的BroadcastRecord r,在7.1中讲过,整个大方法都围绕着这个r进行。如果r为null,那么就该结束了,否则继续往下。
8.7. 接着判断些出错逻辑,如果当前时间超出了2倍的广播超时时间 * 接收者数,那么就弹出ANR。由于这里是特殊出错逻辑,非主流程,所以可无视,跳过此处接着看。
8.8. 接着的两个if判断都是在处理特殊出错逻辑,继续跳过。
8.9. 接下去是一个大的延时广播判断if (!r.deferred),deferred仅在addDeferredBroadcast被赋值为true。此处进入后if后,再调用if (mDispatcher.isDeferringLocked(receiverUid))判断是否需要延期处理,通常被判断为不需要,最后跳出这个大的if判断。
8.10. 继续往下,进行一系列系统广播调试所需的Track与Log等操作后,计算timeout时间后,调用setBroadcastTimeoutLocked
8.10.1. 接下来支线任务到setBroadcastTimeoutLocked中,看到在超时的时间点,将采用Handler发送BROADCAST_TIMEOUT_MSG消息
8.10.2. 在接受BROADCAST_TIMEOUT_MSG Handler消息的之处,看到broadcastTimeoutLocked
8.10.3. 进入broadcastTimeoutLocked,这就挥应第1点了,这个方法进行一系列判断后,最终调用AnrHelper.appNotResponding,走到ANR流程。绕了一大圈,终于找到广播触发ANR的导火索了。
8.10.4. setBroadcastTimeoutLocked在未来的时间点埋了个定时炸弹,如果时间到,还没拆除,就ANR。
8.11. 先回来,上面埋了定时炸弹后,串行广播还没有被发送,所以接着往下看。if判断是否需要直接调用调用receiver(if (nextReceiver instanceof BroadcastFilter)),此处不需要,跳过。
8.12. 接下去又是一个250行的skip判断逻辑,走完后skip = false,无需skip此广播。由于静态注册广播的App如果未启动,系统会将App启动,于是这一系列skip判断逻辑包括判断App是否处于已安装的正常状态,权限上是否允许启动,最后在必要时启动App。
8.13. 最终调用processCurBroadcastLocked进行广播的发送,然后便return结束这个长达700行的大方法。
8.14. processCurBroadcastLocked函数最终通过Binder调用IApplicationThread.scheduleReceiver。
源码:frameworks/base/core/java/android/app/IApplicationThread.aidl
等一下,此处的IApplicationThread仍然是oneway的,也就是说仍然是异步调用的。说好的同步呢?说好的串行呢?ANR定时炸弹何时拆除呢?
源码:frameworks/base/core/java/android/content/BroadcastReceiver.java
而拆掉定时炸弹的工作也是在processNextBroadcastLocked中进行的,其中有cancelBroadcastTimeoutLocked调用,函数实现为移除Handler消息BROADCAST_TIMEOUT_MSG。
采用adb shell dumpsys activity broadcasts可以查看系统为广播设置的超时值(Broadcast parameters)
adb logcat -s ActivityManager可以查看ANR的初步信息,如:
E ActivityManager: ANR in com.huya.mtp.mybroadcast
E ActivityManager: PID: 10653
E ActivityManager: Reason: Broadcast of Intent { act=com.huya.mtp.mybroadcast.BROADCAST_STATIC flg=0x10000010 pkg=com.huya.mtp.mybroadcast cmp=com.huya.mtp.mybroadcast/.MyBroadcastStaticReceiver (has extras) }
在开始Service ANR前,再次同样问个问题:
创建一个Android Hello World工程,写一个服务,在Service.onCreate中调用SystemClock.sleep(300 * 1000),添加一个Button,点击后调用Context.startForegroundService启动这个服务。
运行App,点击一次按钮,一直等待,会不会产生ANR呢?结果是会,但通常看不到弹出ANR框,因为这个时候会崩溃。实际上此时即发生了ANR,又发生了崩溃。
源码:frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
源码:frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
Context.startService --> ContextWrapper.startService --> ContextImpl.startService --> ContextImpl.startServiceCommon --> ActivityManagerService.startService
看到跟Broadcast的流程一摸一样,下面再验证一下Context.startForegroundService,进入调用链:
Context.startForegroundService --> ContextWrapper.startForegroundService --> ContextImpl.startForegroundService --> ContextImpl.startServiceCommon --> ActivityManagerService.startService
发现两者的流程也一致,foreground与否的差别在于startServiceCommon的第二个参数boolean requireForeground。
ActivityManagerService.startService ----> ActiveServices.startServiceLocked
经过重载来到了ActiveServices.startServiceLocked,这个方法大概250行代码,接下来进去看看。
7.1. 判断并设置final boolean callerFg的值,表示调用发起者是否为前台,这个值将贯穿整个Service的启动过程。ActivityManagerService.getRecordForAppLocked用于查找调用发起者的信息。
7.2. 接着调用retrieveServiceLocked查找服务启动接收者的信息,并最终取得ServiceRecord r。跟广播变量名一致,这个r变量是用于记录服务信息的。
7.3. 接下来一系列的判断,最终为ServiceRecord r的成员变量赋值。
7.4. 然后又是一系列的判断,最终来到内部的startServiceInnerLocked方法。
7.4.1. 接下去开启支线任务,进入到startServiceInnerLocked
7.4.2 继续进入,进行一些逻辑处理后,转调bringUpServiceLocked
7.4.3. 继续进入,进行一些逻辑处理后,转调realStartServiceLocked
7.4.4. 继续进入,进行一些逻辑处理后,调用bumpServiceExecutingLocked(此处也能挥应至如上第5点了)。在7.1点中提到boolean callerFg,此值一直传递到bumpServiceExecutingLocked中,最终赋值给r变量的executeFg,在后续设置Service超时时使用。
7.4.5. 继续进入,进行一些判断后,发现调用了scheduleServiceTimeoutLocked,这儿就埋上ANR定时炸弹了,如果是前台服务,超时设置为20秒,后台则为200秒(此处挥应如上第4点)。
7.4.6. 从scheduleServiceTimeoutLocked中退出
7.4.7. 从bumpServiceExecutingLocked中退出
7.4.8. 回到realStartServiceLocked继续往下看。最终通过Binder调用到ActivityThread.scheduleCreateService,回调已经来到Service的目标进程中了。scheduleCreateService发送Handler消息CREATE_SERVICE。
源码:frameworks/base/core/java/android/app/ActivityThread.java
7.4.9. 在接受CREATE_SERVICE处调用handleCreateService。
7.4.10. handleCreateService进行一系列检查后,调用service.onCreate,这就到了目标Service的onCreate方法了,也就是Service的@Override onCreate。
7.4.11. 上述onCreate执行完后,接着往下通过Binder调用到ActivityManagerService.serviceDoneExecuting。
7.4.12. 继续一路转调至ActiveServices.serviceDoneExecutingLocked
7.4.13. 然后再转调至ActiveServices.serviceDoneExecutingLocked,在其中经过一系列判断后,最终调用Handler.removeMessages(ActivityManagerService.SERVICE_TIMEOUT_MSG, r.app)拆除ANR定时炸弹。
7.4.14. 这里补充说明一下。查看源码的同时,有些朋友可能会看到Handler消息ActivityManagerService.SERVICE_FOREGROUND_TIMEOUT_MSG,10秒的超时时间SERVICE_START_FOREGROUND_TIMEOUT,从命名上看,似乎说的是前台服务,而前台服务超时的20秒在7.4.5点已经讲过了,此处的这个10秒的意思是什么呢?实际上,如果调用Context.startForegroundService启动前台服务,则系统要求在这个10秒钟之内,Service必须调用Context.startForeground,否则会产生ANR,也会抛出RuntimeException崩溃。崩溃会将进程退掉,所以表面看上去是发生崩溃了,实际上ANR也发生了,只不过表面上看不到(崩溃是因为发送了Handler消息SCHEDULE_CRASH,代码在ActivityThread.java中)
logcat中的崩溃栈:
2021-11-08 15:20:13.931 11661-11661/com.huchao.myserviceanr E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.huchao.myserviceanr, PID: 11661
android.app.RemoteServiceException: Context.startForegroundService() did not then call Service.startForeground(): ServiceRecord{56a0ba u0 com.huchao.myserviceanr/.MyService}
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2005)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:223)
at android.app.ActivityThread.main(ActivityThread.java:7656)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
logcat中的ANR信息:
E ActivityManager: ANR in com.huchao.myserviceanr
E ActivityManager: PID: 11661
E ActivityManager: Reason: Context.startForegroundService() did not then call Service.startForeground(): ServiceRecord{56a0ba u0 com.huchao.myserviceanr/.MyService}
adb logcat -s ActivityManager可以查看ANR的初步信息,如:
ActivityManager: ANR in com.huchao.myserviceanr
ActivityManager: PID: 11033
ActivityManager: Reason: executing service com.huchao.myserviceanr/.ForegroundService
源码:frameworks/base/services/core/java/com/android/server/am/ContentProviderHelper.java
2.1. ContentProviderHelper中的appNotRespondingViaProvider调用。在ContentProviderHelper类中就有名为getProviderMimeType的方法调用appNotRespondingViaProvider,继续搜索getProviderMimeType,发现在ActivityManagerShellCommand中有调用getProviderMimeType,不过这个类是给adb shell am start-activity使用的,已超出本文范围,所以忽略。
源码:frameworks/base/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
2.2. ActivityManagerService, ActivityThread, ContextImpl中均有appNotRespondingViaProvider调用。不过这3个类仅为转调appNotRespondingViaProvider,并没有触发逻辑,所以先忽略。
源码:
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
frameworks/base/core/java/android/app/ActivityThread.java
frameworks/base/core/java/android/app/ContextImpl.java
2.3. ContentProviderClient中有appNotRespondingViaProvider调用,并且也有触发逻辑,接下去深入进去看看。
2.3.1. 在ContentProviderClient中有NotRespondingRunnable,运行到这个Runnable的run就会调用appNotRespondingViaProvider,调用栈流程为:
NotRespondingRunnable.run --> ApplicationContentResolver.appNotRespondingViaProvider --> ActivityThread.appNotRespondingViaProvider --> ActivityManagerService.appNotRespondingViaProvider --> ContentProviderHelper.appNotRespondingViaProvider --> AnrHelper.appNotResponding
调用流程就回到了最开始产生ANR处。
2.3.2. ContentProviderClient与ContentResolver类似,都是提供给Client使用的,都可以用来获取的数据,差别在于两者的使用场景不同。
2.3.3. 接下来看NotRespondingRunnable的触发逻辑就能找到ANR的产生处了,在名为setDetectNotResponding的方法中找到了new NotRespondingRunnable(),并且ContentProviderClient中还提供了beforeRemote, afterRemote来安装与拆除ANR定时炸弹。看样子是这儿了,继续看setDetectNotResponding方法,这是一个@SystemApi,并且不开放给第三方App使用(通过反射与Hook机制操作系统行为不在本文讨论范围内)。
2.4. 通过如上分析,所有常规三方App可达的ContentProvider ANR可行路径均已被堵死,也就是App自己的ContentProvider是不会产生ANR的。