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);
                                    ......
                    相关文章
                    ANR怎么产生的,怎么分析ANR?(三)
                    ANR怎么产生的,怎么分析ANR?(三)
                    56 0
                    |
                    大数据 数据库 Android开发
                    ANR怎么产生的,怎么分析ANR?(一)
                    ANR怎么产生的,怎么分析ANR?
                    143 0
                    |
                    Java 调度 C++
                    ANR分析总结
                    ANR分析总结
                    1252 0
                    ANR分析总结
                    |
                    存储 监控 Android开发
                    Android卡顿优化 | ANR分析与实战(附ANR-WatchDog源码分析及实战、与AndroidPerformanceMonitor的区别)
                    Android卡顿优化 | ANR分析与实战(附ANR-WatchDog源码分析及实战、与AndroidPerformanceMonitor的区别)
                    |
                    4月前
                    |
                    算法 Java API
                    Android性能优化面试题经典之ANR的分析和优化
                    Android ANR发生于应用无法在限定时间内响应用户输入或完成操作。主要条件包括:输入超时(5秒)、广播超时(前台10秒/后台60秒)、服务超时及ContentProvider超时。常见原因有网络、数据库、文件操作、计算任务、UI渲染、锁等待、ContentProvider和BroadcastReceiver的不当使用。分析ANR可借助logcat和traces.txt。主线程执行生命周期回调、Service、BroadcastReceiver等,避免主线程耗时操作
                    66 3
                    |
                    Android开发
                    为什么会触发ANR,从源码中扒一扒
                    为什么会触发ANR,从源码中扒一扒
                    101 0
                    |
                    6月前
                    |
                    JavaScript IDE Java
                    bugly崩溃排查3:观察是谁调用了崩溃函数
                    bugly崩溃排查3:观察是谁调用了崩溃函数
                    75 0
                    |
                    6月前
                    |
                    缓存 Java 数据库
                    Android 性能优化: 请解释ANR(Application Not Responding)是什么,如何避免它?
                    Android 性能优化: 请解释ANR(Application Not Responding)是什么,如何避免它?
                    111 0
                    |
                    编译器 Android开发
                    深度解读Android崩溃日志案例分析1:so崩溃
                    深度解读Android崩溃日志案例分析1:so崩溃
                    324 1