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

相关推荐
xingpanvip35 分钟前
星盘接口开发文档:组合三限盘接口指南
android·开发语言·前端·python·php·lua
TechMix1 小时前
【fkw学习笔记】Android 13 AOSP 源码添加系统预置应用实战指南
android·笔记·学习
云起SAAS1 小时前
私域直播系统UniApp源码 多商户商城+直播带货 微信小程序+H5+安卓iOS
android·微信小程序·uni-app·私域直播系统
空中海2 小时前
01. 安卓逆向基础、环境搭建与授权
android
星河耀银海2 小时前
JAVA 泛型与通配符:从原理到实战应用
android·java·服务器
Ada大侦探2 小时前
新手小白学习数据分析01----数据分析师???& 数据分析思维学习
android·学习·数据分析
空中海2 小时前
安卓逆向5. 安卓风险防护、加固复测与综合
android
Mr -老鬼2 小时前
EasyClick 双端自动化智能体|Android&iOS 全平台 EC 脚本开发助手
android·ios·自动化·易点云测·#easyclick·#ios自动化
千码君20162 小时前
flutter:与Android Studio模拟器的调试分享
android·flutter
MeAT ITEM3 小时前
MySQL Workbench菜单汉化为中文
android·数据库·mysql