当前位置:   article > 正文

ANR 弹窗的显示原理_activitymanagerservice.show_not_responding_ui_msg

activitymanagerservice.show_not_responding_ui_msg

 目录

基础知识:

实验:

ANR 弹窗的显示原理


思考一个面试题,一个 Service 运行在独立的进程里,在这个 Service 的 onCreate 方法里执行耗时操作会造成 ANR 吗?

直接说结论:会,但是不会有 ANR 的弹窗。

基础知识:

ANR 的四种场景:

  1. Service TimeOut:  service 未在规定时间执行完成:前台服务 20s,后台 200s

  2. BroadCastQueue TimeOut: 未在规定时间内未处理完广播:前台广播 10s 内, 后台 60s 内

  3. ContentProvider TimeOut:  publish 在 10s 内没有完成

  4. Input Dispatching timeout:  5s 内未响应键盘输入、触摸屏幕等事件

ANR 的根本原因是:应用未在规定的时间内处理 AMS 指定的任务才会 ANR。

所以 Service 未在指定时间内执行完成而且非主进程的 Service 仍然需要通过 AMS 进行通信。这也能说明一定会产生 ANR 的。

实验:

理论上是会,那我们就写个小 demo 来试一下。

写一个 Service:

  1. class NoZuoNoDieService: Service() {
  2. override fun onBind(intent: Intent?): IBinder? {
  3. return null
  4. }
  5. override fun onCreate() {
  6. super.onCreate()
  7. // 小睡会模拟耗时
  8. Thread.sleep(21_000)
  9. }
  10. }

然后在 AndroidManifest 里声明在独立进程

  1. <service
  2. android:name=".service.NoZuoNoDieService"
  3. android:process=":whatever"
  4. />

最后我们把这个 Service 调起来作死

startService(Intent(this, NoZuoNoDieService::class.java))

应用并没有 ANR 弹窗,不过 logcat 有 ANR 相关信息,看一下 trace 文件:

  1. ----- pid 14608 at 2021-03-23 19:56:20 -----
  2. Cmd line: demo.com.sam.demofactory:whatever
  3. ... 省略无关信息
  4. "main" prio=5 tid=1 Sleeping
  5. | group="main" sCount=1 dsCount=0 flags=1 obj=0x73f13a80 self=0x78e7cc2a00
  6. | sysTid=14608 nice=0 cgrp=default sched=0/0 handle=0x796c96d9a8
  7. | state=S schedstat=( 57816925 3067496 80 ) utm=2 stm=3 core=4 HZ=100
  8. | stack=0x7fe1d03000-0x7fe1d05000 stackSize=8MB
  9. | held mutexes=
  10. at java.lang.Thread.sleep(Native method)
  11. - sleeping on <0x0a71f3e8> (a java.lang.Object)
  12. at java.lang.Thread.sleep(Thread.java:373)
  13. - locked <0x0a71f3e8> (a java.lang.Object)
  14. at java.lang.Thread.sleep(Thread.java:314)
  15. at demo.com.sam.demofactory.service.NoZuoNoDieService.onCreate(NoZuoNoDieService.kt:15)
  16. at android.app.ActivityThread.handleCreateService(ActivityThread.java:3426)
  17. at android.app.ActivityThread.-wrap4(ActivityThread.java:-1)
  18. at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1744)
  19. at android.os.Handler.dispatchMessage(Handler.java:106)
  20. at android.os.Looper.loop(Looper.java:171)
  21. at android.app.ActivityThread.main(ActivityThread.java:6611)
  22. at java.lang.reflect.Method.invoke(Native method)
  23. at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
  24. at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)

ANR 弹窗的显示原理

我们来看一下这个没有弹窗的 ANR 是怎么发生的

首先来复习一下 说一说 Service 的启动流程

 AMS 真正去启动 Service 调用的是 ActiveServices.realStartServiceLocked 方法:

  1. // API 29 com.android.server.am.ActiveServices
  2. private final void realStartServiceLocked(ServiceRecord r,
  3. ProcessRecord app, boolean execInFg) throws RemoteException {
  4. ...
  5. bumpServiceExecutingLocked(r, execInFg, "create");
  6. }
  7. private final void bumpServiceExecutingLocked(ServiceRecord r, boolean fg, String why) {
  8. ...
  9. scheduleServiceTimeoutLocked(r.app);
  10. }
  11. // 通过 Handler 延时发一条消息,延时时间则为 Service 触发的 ANR 时长
  12. // SERVICE_TIMEOUT = 20*1000
  13. // SERVICE_BACKGROUND_TIMEOUT = SERVICE_TIMEOUT * 10;
  14. void scheduleServiceTimeoutLocked(ProcessRecord proc) {
  15. if (proc.executingServices.size() == 0 || proc.thread == null) {
  16. return;
  17. }
  18. Message msg = mAm.mHandler.obtainMessage(
  19. ActivityManagerService.SERVICE_TIMEOUT_MSG);
  20. msg.obj = proc;
  21. mAm.mHandler.sendMessageDelayed(msg,
  22. proc.execServicesFg ? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT);
  23. }

如果 Service 在规定时间内启动完成,则这个消息会被 remove 掉,我们今天要看一下超时之后,收到这个消息是怎么处理的。

  1. // com.android.server.am.ActivityManagerService.MainHandler
  2. final class MainHandler extends Handler {
  3. @Override
  4. public void handleMessage(Message msg) {
  5. switch (msg.what) {
  6. case SERVICE_TIMEOUT_MSG: {
  7. mServices.serviceTimeout((ProcessRecord)msg.obj);
  8. } break;
  9. }
  10. }

mServices 是 ActiveServices:

  1. // com.android.server.am.ActiveServices#serviceTimeout
  2. void serviceTimeout(ProcessRecord proc) {
  3. ...
  4. if (timeout != null && mAm.mProcessList.mLruProcesses.contains(proc)) {
  5. Slog.w(TAG, "Timeout executing service: " + timeout);
  6. ...
  7. mAm.mHandler.removeCallbacks(mLastAnrDumpClearer);
  8. mAm.mHandler.postDelayed(mLastAnrDumpClearer, LAST_ANR_LIFETIME_DURATION_MSECS);
  9. anrMessage = "executing service " + timeout.shortInstanceName;
  10. }
  11. ...
  12. if (anrMessage != null) {
  13. proc.appNotResponding(null, null, null, null, false, anrMessage);
  14. }
  15. }

appNotResponding 就是最终处理 ANR 逻辑的代码了

  1. // com.android.server.am.ProcessRecord
  2. void appNotResponding(String activityShortComponentName,
  3. ApplicationInfo aInfo,
  4. ...) {
  5. ...
  6. if (isMonitorCpuUsage()) {
  7. mService.updateCpuStatsNow();
  8. }
  9. ...
  10. if (isSilentAnr() && !isDebugging()) {
  11. kill("bg anr", true);
  12. return;
  13. }
  14. // Bring up the infamous App Not Responding dialog
  15. Message msg = Message.obtain();
  16. msg.what = ActivityManagerService.SHOW_NOT_RESPONDING_UI_MSG;
  17. msg.obj = new AppNotRespondingDialog.Data(this, aInfo, aboveSystem);
  18. mService.mUiHandler.sendMessage(msg);
  19. }

如果 isSilentAnr() 为 true,则直接杀死所在进程,不会发送显示 ANR 弹窗的消息。来看下相关方法

  1. boolean isSilentAnr() {
  2. return !getShowBackground() && !isInterestingForBackgroundTraces();
  3. }
  4. private boolean getShowBackground() {
  5. return Settings.Secure.getInt(mService.mContext.getContentResolver(),
  6. Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0;
  7. }
  8. private boolean isInterestingForBackgroundTraces() {
  9. // The system_server is always considered interesting.
  10. if (pid == MY_PID) {
  11. return true;
  12. }
  13. // A package is considered interesting if any of the following is true :
  14. //
  15. // - It's displaying an activity.
  16. // - It's the SystemUI.
  17. // - It has an overlay or a top UI visible.
  18. //
  19. // NOTE: The check whether a given ProcessRecord belongs to the systemui
  20. // process is a bit of a kludge, but the same pattern seems repeated at
  21. // several places in the system server.
  22. return isInterestingToUserLocked() ||
  23. (info != null && "com.android.systemui".equals(info.packageName))
  24. || (hasTopUi() || hasOverlayUi());
  25. }

我们得到两个结论:

  • ANR_SHOW_BACKGROUND 可以在「开发者选项」里配置显示所有“应用程序无响应”,开启后即使是该场景也会有 ANR 弹窗

  • 未开启上述配置情况下,如果是后台进程,则不显示 ANR 弹窗

本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号