【Android】ANR 原理解析(Service)

前言

ANR,Application Not responding,也就是应用程序未响应。

Android 系统对于一些事件需要在一定的时间范围内完成,如果超过预定时间不能得到响应或者响应时间过长,都会造成ANR。

所有与 ANR 相关的消息,都会经过系统进程 system_server 来调度,然后派发到应用进程完成对消息的实际处理, 一旦应用程序处理消息超时,系统就会收集一些系统状态,如 CPU/IO 使用情况、进程函数调用栈,并且报告用户有进程出现 ANR 了,同时会弹出 ANR 的对话框。

ANR机制可以分为两部分:

  • 监测机制:针对不同的 ANR 类型如 Broadcast、Service、InputEvent 都有一套监测机制
  • 报告机制:在监测到ANR以后,需要显示ANR对话框、输出日志

Service ANR 原理

Android 是通过 Handler 机制设置定时消息来实现检测 Service 超时的。

Service 运行在应用程序的主线程,如果在前台进程中执行 Service ,时间超过 20 秒,则会引发ANR,如果在后台进程中执行 Service ,时间超过 200 秒,则会引发ANR。

如何检测 Service 的 ANR 问题,分为两步:

  • 当发生 Service ANR 时,一般可以先排查一下在 Service 的生命周期函数中有没有做耗时操作
  • 若应用层代码逻辑查不出问题,就需要检查当前系统的状态,如 CPU 使用情况、系统服务的状态等,判断当时发生 ANR 的进程是否受到系统运行异常的影响

那么 Service 发生 ANR 的原理是怎样的呢,下面我们将从源码角度上进行分析。

Android Serviced 启动流程

下面我们先分析 Service 的启动流程。

// 点击按钮 - 启动服务
fun clickToBindService(view: View) {
    Intent(this, MyNewService::class.java).also {
        startService(it)
    }
}

点击按钮启动服务的方法中,调用了 startService 方法来启动一个 Service,跟踪该方法,会调用到 ContextImpl.startService 中:

// ContextImpl.java
@Override
public ComponentName startService(Intent service) {
    warnIfCallingFromSystemProcess();
    return startServiceCommon(service, false, mUser);
}

进而继续执行 ContextImpl.startServiceCommon 方法:

// ContextImpl.java
private ComponentName startServiceCommon(Intent service, boolean requireForeground,
        UserHandle user) {
// …………………………………………………………………………………………………………………………………………………………………………………………
        ComponentName cn = ActivityManager.getService().startService(
                mMainThread.getApplicationThread(), service,
                service.resolveTypeIfNeeded(getContentResolver()), requireForeground,
                getOpPackageName(), getAttributionTag(), user.getIdentifier());
// …………………………………………………………………………………………………………………………………………………………………………………………
}

ContextImpl.startServiceCommon 方法中可以看到,执行了 ActivityManagerService,即 AMSstartService 方法,跟踪该方法:

// ActivityManagerService.java
@Override
public ComponentName startService(IApplicationThread caller, Intent service,
        String resolvedType, boolean requireForeground, String callingPackage,
        String callingFeatureId, int userId)
        throws TransactionTooLargeException {
         UserHandle user) {
// …………………………………………………………………………………………………………………………………………………………………………………………
            res = mServices.startServiceLocked(caller, service,
                    resolvedType, callingPid, callingUid,
                    requireForeground, callingPackage, callingFeatureId, userId);
        UserHandle user) {
// …………………………………………………………………………………………………………………………………………………………………………………………
        return res;
    }
}

可见,调用了 mServices.startServiceLocked 方法,而这个 mServices 则是一个 ActiveServices 对象,因此,代码继续执行 ActiveServices.startServiceLocked 方法:

// ActivityService.java
ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
        int callingPid, int callingUid, boolean fgRequired, String callingPackage,
        @Nullable String callingFeatureId, final int userId)
        throws TransactionTooLargeException {
    return startServiceLocked(caller, service, resolvedType, callingPid, callingUid, fgRequired,
            callingPackage, callingFeatureId, userId, false, null);
}

在 ActivityService 中,经过一系列的调用,会执行到 ActivityService.realStartServiceLocked 方法

private void realStartServiceLocked(ServiceRecord r, ProcessRecord app,
        IApplicationThread thread, int pid, UidRecord uidRecord, boolean execInFg,
        boolean enqueueOomAdj) throws RemoteException {
    // ............................................................
    // 设置ANR超时,可见在正式启动 Service之前,会开始 ANR 的监测
    bumpServiceExecutingLocked(r, execInFg, "create", null /* oomAdjReason */);
    // ............................................................
    // 启动 Service 
    thread.scheduleCreateService(r, r.serviceInfo,
                mAm.compatibilityInfoForPackage(r.serviceInfo.applicationInfo),
                app.mState.getReportedProcState());
    // ............................................................  
    // 调动 Service 的其他方法,如 onStartCommand
    sendServiceArgsLocked(r, execInFg, true);
}

在 realStartServiceLocked 方法中,执行到了 ApplicationThread.scheduleCreateService 方法(这里就不看了),最终又会执行到 ActivityThread.scheduleCreateService 方法,如下:

// ActivityThread.java
public final void scheduleCreateService(IBinder token,
        ServiceInfo info, CompatibilityInfo compatInfo, int processState) {
    updateProcessState(processState, false);
    CreateServiceData s = new CreateServiceData();
    s.token = token;
    s.info = info;
    s.compatInfo = compatInfo;
	// 通过 handler 发送创建 Service 的消息
    sendMessage(H.CREATE_SERVICE, s);
}

ActivityThread.scheduleCreateService 方法中,会通过 handler 发送一个创建 Service 的消息:CREATE_SERVICE,当接收到这个消息时,会执行 ActivityThread.handleCreateService 方法,最终会执行到 service.onCreate() 方法,进而执行 Service 的声明周期,如下所示:

// ActivityThread.java
public void handleMessage(Message msg) {
    if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
    switch (msg.what) {
        // ..........................................
        case CREATE_SERVICE:
            handleCreateService((CreateServiceData)msg.obj);
            break;
    }
    // ..........................................
}

// 最终执行到这里,调用 service.onCreate() 方法
private void handleCreateService(CreateServiceData data) {
// ..........................................
            service.onCreate();
// ..........................................
    }

以上就是 Service 的启动流程了,整体还是比较清晰的。

Service 超时监测

Service 超时监测机制可以从 Service 启动流程中找到。

在上述 Service 启动流程中,我们跟踪执行到了 ActivityService.realStartServiceLocked 方法,在该方法中,通过调用 bumpServiceExecutingLocked 方法来开始监测 ANR,代码如下:

// ActivityService.java:
private boolean bumpServiceExecutingLocked(ServiceRecord r, boolean fg, String why,
        @Nullable String oomAdjReason) {
// ..........................................
    scheduleServiceTimeoutLocked(r.app);
// ..........................................
}

bumpServiceExecutingLocked 方法会继续调用到 scheduleServiceTimeoutLocked 方法:

void scheduleServiceTimeoutLocked(ProcessRecord proc) {
    if (proc.mServices.numberOfExecutingServices() == 0 || proc.getThread() == null) {
        return;
    }
    Message msg = mAm.mHandler.obtainMessage(
            ActivityManagerService.SERVICE_TIMEOUT_MSG);
    msg.obj = proc;
    // 延时指定时间后,发送消息:SERVICE_TIMEOUT_MSG
    // 前台进程中执行 Service,SERVICE_TIMEOUT = 20s
    // 后台进程中执行 Service,SERVICE_BACKGROUND_TIMEOUT = 200s
    mAm.mHandler.sendMessageDelayed(msg, proc.mServices.shouldExecServicesFg()
            ? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT);
}

如果在指定的时间内没有将 SERVICE_TIMEOUT_MSG 这个消息 remove 掉,则该消息会在 ActivityManagerService 中处理:

// ActivityManagerService.java:
final class MainHandler extends Handler {
    // ..........................................
    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
        // ..........................................
        case SERVICE_TIMEOUT_MSG: {
            mServices.serviceTimeout((ProcessRecord) msg.obj);
        } break;
        case SERVICE_FOREGROUND_TIMEOUT_MSG: {
            mServices.serviceForegroundTimeout((ServiceRecord) msg.obj);
        } break;
        // ..........................................
    }
}

这里我们看到,会调用 mServices.serviceTimeout() 方法,而 mServices 指的是 ActivityService,因此我们进入 ActivityService.serviceTimeout 方法中:

// ActivityService.java:
void serviceTimeout(ProcessRecord proc) {
        String anrMessage = null;
        synchronized(mAm) {
            if (proc.isDebugging()) {
                // 应用程序正在调试,忽略超时
                return;
            }
            final ProcessServiceRecord psr = proc.mServices;
            if (psr.numberOfExecutingServices() == 0 || proc.getThread() == null) {
                return;
            }
            final long now = SystemClock.uptimeMillis();
            final long maxTime =  now -
                    (psr.shouldExecServicesFg() ? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT);
            ServiceRecord timeout = null;
            long nextTime = 0;
            // 遍历所有正在执行的服务,寻找运行超时的 Service
            for (int i = psr.numberOfExecutingServices() - 1; i >= 0; i--) {
                ServiceRecord sr = psr.getExecutingServiceAt(i);
                if (sr.executingStart < maxTime) {
                    timeout = sr;
                    break;
                }
                if (sr.executingStart > nextTime) {
                    nextTime = sr.executingStart;
                }
            }
            // 判断执行 Service 超时的进程是否在最近运行进程列表,如果不在,则忽略这个ANR
            if (timeout != null && mAm.mProcessList.isInLruListLOSP(proc)) {
                Slog.w(TAG, "Timeout executing service: " + timeout);
                StringWriter sw = new StringWriter();
                PrintWriter pw = new FastPrintWriter(sw, false, 1024);
                pw.println(timeout);
                timeout.dump(pw, "    ");
                pw.close();
                mLastAnrDump = sw.toString();
                mAm.mHandler.removeCallbacks(mLastAnrDumpClearer);
                mAm.mHandler.postDelayed(mLastAnrDumpClearer, LAST_ANR_LIFETIME_DURATION_MSECS);
                anrMessage = "executing service " + timeout.shortInstanceName;
            } else {
                Message msg = mAm.mHandler.obtainMessage(
                        ActivityManagerService.SERVICE_TIMEOUT_MSG);
                msg.obj = proc;
                mAm.mHandler.sendMessageAtTime(msg, psr.shouldExecServicesFg()
                        ? (nextTime+SERVICE_TIMEOUT) : (nextTime + SERVICE_BACKGROUND_TIMEOUT));
            }
        }
		// 当存在timeout的service,则执行appNotResponding,报告ANR
        if (anrMessage != null) {
            mAm.mAnrHelper.appNotResponding(proc, anrMessage);
        }
    }

可见,在该方法的最后,如果存在 ANR,则会执行到 AnrHelper.appNotResponding 方法来报告一个 ANR。

以上就是本文的所有内容,下一篇文章将会继续从源码的角度上探究 输入事件处理超时 引入的 ANR 原理。

  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一场雪ycx

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值