性能优化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一般会通过Context的startService启动,然后通过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;

最终还是调用了 ActiveServices 的 serviceTimeout 方法

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系列

相关推荐
程序员老刘19 分钟前
从短剧App的bug看响应式编程在AI时代的优势
flutter·ai编程·客户端
方白羽1 小时前
Android 双开环境底层剖析与风控检测落地
android·安全·app
weixin_563371472 小时前
Android Studio 2024 版 Device Explorer 打开方法
android·ide·android studio
AIsoft_86894 小时前
职场音频文件怎么整理?录音APP有哪些用法
android·音视频
booboosui5 小时前
第一批个人 Agent 用户正在卸载,问题不在模型,在它住在哪儿
android
洞见前行5 小时前
点击图标到内容出现,你的 App 在这几秒里做了什么:Android 冷启动全链路优化实战
android·性能优化·android studio
凯基迪科技5 小时前
芯片UID绑定修改 加密与逆向修改思路分析
android
Liben6 小时前
Weex开发踩坑记录
android·前端·ios
救救孩子把8 小时前
结构化输出为何“格式正确、答案错误”?JSON Schema 的能力边界
android·java·json·agent·agent服务
张小姐的猫9 小时前
【MySQL】事务
android·mysql·adb