性能优化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系列

相关推荐
Meteors.27 分钟前
安卓源码阅读——01.grade设置binding为true时,xml如何进行映射
android·xml
_李小白1 小时前
【android opencv学习笔记】Day 26: 滤波算法之低通滤波与图像缩放插值
android·opencv·学习
NiceCloud喜云2 小时前
Claude Code Routines 实战:三种触发器跑通云端自动化编码
android·运维·数据库·人工智能·自动化·json·飞书
我命由我123455 小时前
Bugly - Bugly 基本使用( App 质量追踪平台)
android·java·java-ee·android studio·android jetpack·android-studio·android runtime
weiggle5 小时前
第二篇:搭建你的第一个 Compose 项目——开发环境与项目结构
android·前端
阿巴斯甜6 小时前
为什么 AIDL 接口客户端、服务端要写两份一模一样的?
android
weiggle6 小时前
第一篇:Jetpack Compose 宣言——为什么 Android 开发需要声明式 UI
android
城管不管8 小时前
什么是Prompt?
android·java·数据库·语言模型·llm·prompt
weiggle8 小时前
Jetpack Compose 重组机制与性能优化深度剖析
android