性能优化ANR系列之-Service ANR原理

公众号:代码与生活手记,持续分享Android进阶知识,App性能优化,Framework学习,应用架构设计
文章基于Android14,目录:

  1. 埋炸弹(开启定时检测任务)
  2. 拆炸弹(取消定时任务)
  3. 引爆炸弹(执行消息)
  4. 有用的日志

众所皆知,Service主要有两类:

  1. 前台服务,则超时为 SERVICE_TIMEOUT = 20s;
  2. 后台服务,则超时为 SERVICE_BACKGROUND_TIMEOUT = 200s

源码中的定义:

java 复制代码
    private static final long DEFAULT_SERVICE_TIMEOUT = 20 * 1000 * Build.HW_TIMEOUT_MULTIPLIER;
    private static final long DEFAULT_SERVICE_BACKGROUND_TIMEOUT = DEFAULT_SERVICE_TIMEOUT * 10;

埋炸弹(开启定时检测任务)

Service一般会通过ContextstartService启动,然后通过AMS一路走到system_server 进程,最后一路走到ActiveServices(不同版本可能不一样)的 realStartServiceLocked()方法来埋下炸弹.

java 复制代码
    private void realStartServiceLocked(ServiceRecord r, ProcessRecord app,
            IApplicationThread thread, int pid, UidRecord uidRecord, boolean execInFg,
            boolean enqueueOomAdj) throws RemoteException {
       .......
       // 开启anr定时任务
        bumpServiceExecutingLocked(r, execInFg, "create",
                OOM_ADJ_REASON_NONE /* use "none" to avoid extra oom adj */);

        try {
            ......
            //启动service
            thread.scheduleCreateService(r, r.serviceInfo,
                    null /* compatInfo (unused but need to keep method signature) */,
                    app.mState.getReportedProcState());
            created = true;
        }
        .......
    }

首先会调用bumpServiceExecutingLocked方法来开启一个SERVICE_TIMEOUT_MSG定时任务

java 复制代码
    private boolean bumpServiceExecutingLocked(
            ServiceRecord r, boolean fg, String why, @OomAdjReason int oomAdjReason) {
        .......
        scheduleServiceTimeoutLocked(r.app);
        ......
    }
    void scheduleServiceTimeoutLocked(ProcessRecord proc) {
        Message msg = mAm.mHandler.obtainMessage(
                ActivityManagerService.SERVICE_TIMEOUT_MSG);
        msg.obj = proc;
        mAm.mHandler.sendMessageDelayed(msg, proc.mServices.shouldExecServicesFg()
                ? mAm.mConstants.SERVICE_TIMEOUT : mAm.mConstants.SERVICE_BACKGROUND_TIMEOUT);
    }

总结: 1、在启动Service之前启动一个SERVICE_TIMEOUT_MSG的定时任务,用来检测ANR 2、该任务会根据Service是否是前后台进程,定义不同的ANR时间

拆炸弹(取消定时任务)

在启动 Service 过程中,会经过一系列的调用,走回到应用进程的 ActivityThread handleCreateService 方法,如下:

java 复制代码
    @UnsupportedAppUsage
    private void handleCreateService(CreateServiceData data) {

        Service service = null;
        try {
            .......
            context.setOuterContext(service);
            // 执行Service的创建和attach
            service.attach(context, this, data.info.name, data.token, app,
                    ActivityManager.getService());
            ......
            service.onCreate();
           .......
            try {
                // service启动完成
                ActivityManager.getService().serviceDoneExecuting(
                        data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        }
        .......

    }

这里通过 AMS 又回到了 system_server 中的 serviceDoneExecuting,如下:

java 复制代码
    private void serviceDoneExecutingLocked(ServiceRecord r, boolean inDestroying,
            boolean finishing, boolean enqueueOomAdj, @OomAdjReason int oomAdjReason) {
        .......
        mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_TIMEOUT_MSG, r.app);
        .......
    }

总结: 1、system_server中经过一系列的调用,回到应用进程的 ActivityThread handleCreateService 方法。这个方法创建Service对象,并调用onCreate方法 2、Service执行完成后,就会调用serviceDoneExecuting通知AMS执行完成,最终会取消SERVICE_TIMEOUT_MSG任务

注意:这里处理 Handler 对应的线程用的是 AMS 中的HandlerThread,和广播用的是同一个线程

引爆炸弹(执行消息)

如果我们没有及时的取消 SERVICE_TIMEOUT_MSG 任务,时间到了之后,就会执行该消息,如下:

java 复制代码
        public void handleMessage(Message msg) {
            switch (msg.what) {
           .......
            case SERVICE_TIMEOUT_MSG: {
                mServices.serviceTimeout((ProcessRecord) msg.obj);
            } break;

最终还是调用了 ActiveServicesserviceTimeout 方法

java 复制代码
    void serviceTimeout(ProcessRecord proc) {
        try {
            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceTimeout()");
            TimeoutRecord timeoutRecord = null;
            synchronized (mAm) {
                ......
                // 打印timeout日志
                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);
                    String anrMessage = "executing service " + timeout.shortInstanceName;
                    timeoutRecord = TimeoutRecord.forServiceExec(anrMessage);
                } ......
            }
            // 处理anr信息的收集
            if (timeoutRecord != null) {
                mAm.mAnrHelper.appNotResponding(proc, timeoutRecord);
            }
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        }
    }

总结: 1、如果任务 SERVICE_TIMEOUT_MSG 没有取消,则会执行该任务 2、调用调用了 ActiveServices serviceTimeout 方法,打印 Timeout executing service 日志 3、调用 mAm.mAnrHelper.appNotResponding 收集 ANR 信息

涉及的日志

Timeout executing service 日志打印 这里的 timeout 是一个 ServiceRecord 对象,这个时间是最接近 anr 的发生时间的。

java 复制代码
Slog.w(TAG, "Timeout executing service: " + timeout);

ANR系列文章性能优化之ANR系列-BroadCastReceiver ANR原理 ANR系列之-ContentProvider为什么ANR?

欢迎阅读和关注ANR系列

相关推荐
_一条咸鱼_36 分钟前
Android Gson基础数据类型转换逻辑(6)
android·面试·gson
_一条咸鱼_1 小时前
Android Runtime并发标记与三色标记法实现原理(55)
android·面试·android jetpack
盗骊3 小时前
Android 终端模拟器 termux app
android
androidwork3 小时前
Kotlinx序列化多平台兼容性详解
android·java·kotlin
移动开发者1号6 小时前
Android存储访问框架(SAF)
android·kotlin
移动开发者1号6 小时前
深入理解文件存储沙盒机制
android·kotlin
云博客-资源宝15 小时前
Android Manifest 权限描述大全
android·开发语言·php
xzkyd outpaper15 小时前
Android DataBinding 与 MVVM
android·计算机八股
zzq199616 小时前
Android 14.0 framework默认将三按钮的导航栏修改为手势导航。
android
ii_best16 小时前
[按键精灵安卓/ios脚本插件开发] 遍历获取LuaAuxLib函数库命令辅助工具
android·ios