ANR怎么产生的,怎么分析ANR?(二)

简介: ANR怎么产生的,怎么分析ANR?(二)

最终在 AppErrors.appNotResponding() 中打印 log 信息、dump 栈信息、打印 CPU 信息。Broadcast Timeout

Android 的广播机制在接收到广播进行处理时,可能会出现 receiver 处理很慢从而影响后续 receiver 接收的情形。因此,Android 对广播的接收处理增加了一个限定时间,超出限定时间将触发 ANR。需要说明,广播超时值会出现在串行广播中。并行广播因为并不存在传输的依赖关系,所以不会发生广播超时。对于不同的广播存在两个限定时间:前台广播10s和后台广播60s。

frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
    // 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;
    ......
    public ActivityManagerService(Context systemContext) {
        ......
        mHandlerThread = new ServiceThread(TAG,
                THREAD_PRIORITY_FOREGROUND, false /*allowIo*/);
        mHandlerThread.start();
        mHandler = new MainHandler(mHandlerThread.getLooper());
        ......
        // 创建广播队列,前台和后台
        mFgBroadcastQueue = new BroadcastQueue(this, mHandler,
                "foreground", BROADCAST_FG_TIMEOUT, false);
        mBgBroadcastQueue = new BroadcastQueue(this, mHandler,
                "background", BROADCAST_BG_TIMEOUT, true);
        mBroadcastQueues[0] = mFgBroadcastQueue;
        mBroadcastQueues[1] = mBgBroadcastQueue;
        ......
    }

广播队列创建时会设置超时时间,接下来直接看下广播的处理过程。

frameworks/base/services/core/java/com/android/server/am/BroadcastQueue.java 
    final void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj) {
        BroadcastRecord r;
        ......
        // 处理并行广播
        while (mParallelBroadcasts.size() > 0) {
            ......
        }
        // 广播正在处理时,检查进程是否存活
        if (mPendingBroadcast != null) {
            ......
        }
        boolean looped = false;
        // 处理串行广播
        do {
            ......
            r = mOrderedBroadcasts.get(0);
            boolean forceReceive = false;
            // 如果广播处理超时,强行结束它
            int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;
            if (mService.mProcessesReady && r.dispatchTime > 0) {
                long now = SystemClock.uptimeMillis();
                if ((numReceivers > 0) &&
                        (now > r.dispatchTime + (2*mTimeoutPeriod*numReceivers))) {
                    broadcastTimeoutLocked(false); // forcibly finish this broadcast
                    forceReceive = true;
                    r.state = BroadcastRecord.IDLE;
                }
            }
            ......
            if (r.receivers == null || r.nextReceiver >= numReceivers
                    || r.resultAbort || forceReceive) {
                if (r.resultTo != null) {
                    try {
                        // 处理完广播,发送最终结果
                        performReceiveLocked(r.callerApp, r.resultTo,
                            new Intent(r.intent), r.resultCode,
                            r.resultData, r.resultExtras, false, false, r.userId);
                        r.resultTo = null;
                    ......
                }
                // 撤销 BROADCAST_TIMEOUT_MSG 消息
                cancelBroadcastTimeoutLocked();
                ....
            }
        } while (r == null);
        // 获取下一个广播
        int recIdx = r.nextReceiver++;
        r.receiverTime = SystemClock.uptimeMillis();
        if (recIdx == 0) {
            // 在 receiver 启动时开启跟踪
            r.dispatchTime = r.receiverTime;
            r.dispatchClockTime = System.currentTimeMillis();
            ......
        }
        if (! mPendingBroadcastTimeoutMessage) {
            // 设置广播超时时间,发送 BROADCAST_TIMEOUT_MSG
            long timeoutTime = r.receiverTime + mTimeoutPeriod;
            setBroadcastTimeoutLocked(timeoutTime);
        }
        final BroadcastOptions brOptions = r.options;
        final Object nextReceiver = r.receivers.get(recIdx);
        // 处理动态注册的广播
        if (nextReceiver instanceof BroadcastFilter) {
            ......
        }
        // 处理静态注册的广播
        ResolveInfo info =
            (ResolveInfo)nextReceiver;
        ComponentName component = new ComponentName(
                info.activityInfo.applicationInfo.packageName,
                info.activityInfo.name);
        ....
        // 获取 receiver 对应的进程
        String targetProcess = info.activityInfo.processName;
        ProcessRecord app = mService.getProcessRecordLocked(targetProcess,
                info.activityInfo.applicationInfo.uid, false);
        ......
        // 如果相应进程存在,直接进行处理
        if (app != null && app.thread != null && !app.killed) {
            try {
                app.addPackage(info.activityInfo.packageName,
                        info.activityInfo.applicationInfo.versionCode, mService.mProcessStats);
                processCurBroadcastLocked(r, app, skipOomAdj);
                return;
            } catch (RemoteException e) {
            } catch (RuntimeException e) {.
                logBroadcastReceiverDiscardLocked(r);
                finishReceiverLocked(r, r.resultCode, r.resultData,
                        r.resultExtras, r.resultAbort, false);
                scheduleBroadcastsLocked();
                r.state = BroadcastRecord.IDLE;
                return;
            }
        }
        // 如果相应进程不存在,则创建进程
        if ((r.curApp=mService.startProcessLocked(targetProcess,
                info.activityInfo.applicationInfo, true,
                r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND,
                "broadcast", r.curComponent,
                (r.intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, false, false))
                        == null) {
            logBroadcastReceiverDiscardLocked(r);
            finishReceiverLocked(r, r.resultCode, r.resultData,
                    r.resultExtras, r.resultAbort, false);
            scheduleBroadcastsLocked();
            r.state = BroadcastRecord.IDLE;
            return;
        }
        mPendingBroadcast = r;
        mPendingBroadcastRecvIndex = recIdx;
    }

上述代码是广播处理的简单流程,与 ANR 触发相关的主要是两个地方,

  • 设置广播超时的消息,setBroadcastTimeoutLocked()
  • 撤销广播超时消息,cancelBroadcastTimeoutLocked()

在开始处理一个广播时,会根据超时时间来设置一个延迟发送的消息。在限定时间内,如果该消息没有被撤销就会触发 ANR。

    frameworks/base/services/core/java/com/android/server/am/BroadcastQueue.java
        final void setBroadcastTimeoutLocked(long timeoutTime) {
            if (! mPendingBroadcastTimeoutMessage) {
                // 发送延迟消息 BROADCAST_TIMEOUT_MSG
                Message msg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG, this);
                mHandler.sendMessageAtTime(msg, timeoutTime);
                mPendingBroadcastTimeoutMessage = true;
            }
        }
      frameworks/base/services/core/java/com/android/server/am/BroadcastQueue.java
          private final class BroadcastHandler extends Handler {
              public BroadcastHandler(Looper looper) {
                  super(looper, null, true);
              }
              @Override
              public void handleMessage(Message msg) {
                  switch (msg.what) {
                      ....
                      case BROADCAST_TIMEOUT_MSG: {
                          synchronized (mService) {
                              broadcastTimeoutLocked(true);
                          }
                      } break;
                  }
              }
          }
          ......
          final void broadcastTimeoutLocked(boolean fromMsg) {
              ......
              long now = SystemClock.uptimeMillis();
              BroadcastRecord r = mOrderedBroadcasts.get(0);
              if (fromMsg) {
                  ......
                  long timeoutTime = r.receiverTime + mTimeoutPeriod;
                  if (timeoutTime > now) {
                      // 如果当前时间没有达到限定时间,重新设置超时消息
                      setBroadcastTimeoutLocked(timeoutTime);
                      return;
                  }
              }
              BroadcastRecord br = mOrderedBroadcasts.get(0);
              if (br.state == BroadcastRecord.WAITING_SERVICES) {
                  // 广播已经处理完,正在等待 service 执行。继续处理下一条广播
                  br.curComponent = null;
                  br.state = BroadcastRecord.IDLE;
                  processNextBroadcast(false);
                  return;
              }
              ......
              // 获取 APP 进程
              if (curReceiver != null && curReceiver instanceof BroadcastFilter) {
                  BroadcastFilter bf = (BroadcastFilter)curReceiver;
                  if (bf.receiverList.pid != 0
                          && bf.receiverList.pid != ActivityManagerService.MY_PID) {
                      synchronized (mService.mPidsSelfLocked) {
                          app = mService.mPidsSelfLocked.get(
                                  bf.receiverList.pid);
                      }
                  }
              } else {
                  app = r.curApp;
              }
              ......
              // 继续处理下一条广播
              finishReceiverLocked(r, r.resultCode, r.resultData,
                      r.resultExtras, r.resultAbort, false);
              scheduleBroadcastsLocked();
              if (!debugging && anrMessage != null) {
                  // 触发 ANR
                  mHandler.post(new AppNotResponding(app, anrMessage));
              }
          }

      在广播处理完成时会撤销超时消息。

         final void cancelBroadcastTimeoutLocked() {
                if (mPendingBroadcastTimeoutMessage) {
                    mHandler.removeMessages(BROADCAST_TIMEOUT_MSG, this);
                    mPendingBroadcastTimeoutMessage = false;
                }
            }

        Service Timeout

        Service Timeout 发生在 Service 的启动过程中,如果在限定时间内无法完成启动就会触发 ANR。根据 Service 类型的不同,赋予前台服务和后台服务不同的超时时间。

          frameworks/base/services/core/java/com/android/server/am/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;

          在 Service 的启动过程中,会根据限定时间来设置一个延迟消息,用来触发启动超时。

            frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
                private final void bumpServiceExecutingLocked(ServiceRecord r, boolean fg, String why) {
                    ......
                            scheduleServiceTimeoutLocked(r.app);
                    ......
                }
                ......
                private final void realStartServiceLocked(ServiceRecord r,
                        ProcessRecord app, boolean execInFg) throws RemoteException {
                    ......
                    r.app = app;
                    r.restartTime = r.lastActivity = SystemClock.uptimeMillis();
                    final boolean newService = app.services.add(r);
                    bumpServiceExecutingLocked(r, execInFg, "create"); // 发送超时消息
                    mAm.updateLruProcessLocked(app, false, null); // 更新 LRU
                    updateServiceForegroundLocked(r.app, /* oomAdj= */ false);
                    mAm.updateOomAdjLocked(); // 更新 OOM ADJ
                    boolean created = false;
                    try {
                        ......
                        mAm.notifyPackageUse(r.serviceInfo.packageName,
                                             PackageManager.NOTIFY_PACKAGE_USE_SERVICE);
                        app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
                        // 最终执行服务的 onCreate() 方法
                        app.thread.scheduleCreateService(r, r.serviceInfo,
                                mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
                                app.repProcState);
                        r.postNotification();
                        created = true;
                    ......
                }
                ......
                void scheduleServiceTimeoutLocked(ProcessRecord proc) {
                    if (proc.executingServices.size() == 0 || proc.thread == null) {
                        return;
                    }
                    // 设置延迟消息,用于触发服务启动超时
                    Message msg = mAm.mHandler.obtainMessage(
                            ActivityManagerService.SERVICE_TIMEOUT_MSG);
                    msg.obj = proc;
                    mAm.mHandler.sendMessageDelayed(msg,
                            proc.execServicesFg ? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT);
                }

            当服务启动超时,会向 AMS 发送一个 SERVICE_TIMEOUT_MSG 消息。

              frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
                  final class MainHandler extends Handler {
                      public MainHandler(Looper looper) {
                          super(looper, null, true);
                      }
                      @Override
                      public void handleMessage(Message msg) {
                          switch (msg.what) {
                          ......
                          case SERVICE_TIMEOUT_MSG: {
                              mServices.serviceTimeout((ProcessRecord)msg.obj);
                          } break;
                          ......
                          }
                      }
                  };
                frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
                    void serviceTimeout(ProcessRecord proc) {
                        String anrMessage = null;
                        synchronized(mAm) {
                            if (proc.executingServices.size() == 0 || proc.thread == null) {
                                return;
                            }
                            final long now = SystemClock.uptimeMillis();
                            final long maxTime =  now -
                                    (proc.execServicesFg ? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT);
                            ServiceRecord timeout = null;
                            long nextTime = 0;
                            for (int i=proc.executingServices.size()-1; i>=0; i--) {
                                // 寻找超时的服务
                                ......
                            }
                            if (timeout != null && mAm.mLruProcesses.contains(proc)) {
                                // 服务超时,生成超时信息
                                ......
                                mAm.mHandler.removeCallbacks(mLastAnrDumpClearer);
                                mAm.mHandler.postDelayed(mLastAnrDumpClearer, LAST_ANR_LIFETIME_DURATION_MSECS);
                                anrMessage = "executing service " + timeout.shortName;
                            } else {
                                // 服务未超时,重置超时信息
                                Message msg = mAm.mHandler.obtainMessage(
                                        ActivityManagerService.SERVICE_TIMEOUT_MSG);
                                msg.obj = proc;
                                mAm.mHandler.sendMessageAtTime(msg, proc.execServicesFg
                                        ? (nextTime+SERVICE_TIMEOUT) : (nextTime + SERVICE_BACKGROUND_TIMEOUT));
                            }
                        }
                        if (anrMessage != null) {
                            // 触发 ANR
                            mAm.mAppErrors.appNotResponding(proc, null, null, false, anrMessage);
                        }
                    }

                上面描述了 Service Timeout 的产生过程,如果想避免超时消息的产生,就需要在限定时间内将消息移除。移除操作在服务启动完成后进行,下面看一下真正进行服务启动的代码。

                  frameworks/base/core/java/android/app/ActivityThread.java
                      private void handleCreateService(CreateServiceData data) {
                          unscheduleGcIdler();
                          LoadedApk packageInfo = getPackageInfoNoCheck(
                                  data.info.applicationInfo, data.compatInfo);
                          Service service = null;
                          try {
                              // 创建 service
                              java.lang.ClassLoader cl = packageInfo.getClassLoader();
                              service = packageInfo.getAppFactory()
                                      .instantiateService(cl, data.info.name, data.intent);
                          ......
                          try {
                              // 创建ContextImpl对象
                              ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
                              context.setOuterContext(service);
                              // 创建Application对象
                              Application app = packageInfo.makeApplication(false, mInstrumentation);
                              service.attach(context, this, data.info.name, data.token, app,
                                      ActivityManager.getService());
                              service.onCreate(); //     调用服务onCreate()方法
                              mServices.put(data.token, service);
                              try {
                                  // 服务启动完成
                                  ActivityManager.getService().serviceDoneExecuting(
                                          data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
                              ......
                      }
                    frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
                        private void serviceDoneExecutingLocked(ServiceRecord r, boolean inDestroying,
                                boolean finishing) {
                            r.executeNesting--;
                            if (r.executeNesting <= 0) {
                                if (r.app != null) {
                                    r.app.execServicesFg = false;
                                    r.app.executingServices.remove(r);
                                    if (r.app.executingServices.size() == 0) {
                                        // 当前进程中没有正在执行的 service 时,移除服务超时消息
                                        mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_TIMEOUT_MSG, r.app);
                                    ......
                    相关文章
                    |
                    大数据 数据库 Android开发
                    ANR怎么产生的,怎么分析ANR?(一)
                    ANR怎么产生的,怎么分析ANR?
                    137 0
                    ANR怎么产生的,怎么分析ANR?(三)
                    ANR怎么产生的,怎么分析ANR?(三)
                    56 0
                    |
                    Java 调度 C++
                    ANR分析总结
                    ANR分析总结
                    1250 0
                    ANR分析总结
                    |
                    存储 监控 Android开发
                    Android卡顿优化 | ANR分析与实战(附ANR-WatchDog源码分析及实战、与AndroidPerformanceMonitor的区别)
                    Android卡顿优化 | ANR分析与实战(附ANR-WatchDog源码分析及实战、与AndroidPerformanceMonitor的区别)
                    |
                    Android开发
                    为什么会触发ANR,从源码中扒一扒
                    为什么会触发ANR,从源码中扒一扒
                    98 0
                    |
                    6月前
                    |
                    JavaScript IDE Java
                    bugly崩溃排查3:观察是谁调用了崩溃函数
                    bugly崩溃排查3:观察是谁调用了崩溃函数
                    66 0
                    |
                    6月前
                    |
                    缓存 Java 数据库
                    Android 性能优化: 请解释ANR(Application Not Responding)是什么,如何避免它?
                    Android 性能优化: 请解释ANR(Application Not Responding)是什么,如何避免它?
                    110 0
                    |
                    Web App开发 缓存 监控
                    一文教你轻松搞定ANR异常捕获与分析方法
                    选择一款有超强捕获能力的专业产品,对于开发者定位和修复稳定性问题至关重要。友盟+U-APM SDK集成了UC 内核团队强大的技术及友盟+超强的错误捕获能力,通过数万次捕获实践中积累了丰富经验,在产品、性能和研发能力上都极大保障了开发者定位和修复稳定性问题的超强效率。
                    一文教你轻松搞定ANR异常捕获与分析方法
                    |
                    Android开发 算法
                    ANR简介以及解决方案
                    同步发布在:http://snowdream.github.io/blog/2016/02/25/anr-introduce-and-solutions/ ANR ANR,英文全称为 Application Not Responding,即应用无响应。 具体表现,弹出一个应用无响应的窗口,
                    3624 0