ANR系列之五:Service类型ANR原理讲解

news2024/12/26 13:49:07

前言:

ANR系列文章一共有有若干篇,

遵循这样的一个顺序:

1.先讲ANR的基本概念以及发生后的流程;

2.四种类型的ANR是如何发生的;

3.该如何排查和解决ANR类型问题。

想看整个系列的文章,可以参考该系列文章第一篇,里面会有明确的清单:

ANR系列之一:ANR显示和日志生成原理讲解

本篇是ANR系列文章的第五篇,本文主要讲解service类型的ANR类型是如何发生的。

本文主要讲解内容如下:

1.service类型的ANR在系统侧是如何触发的;

2.service类型的ANR在APP侧的执行流程。

3.什么场景下,可以触发service类型的ANR。

PS:阅读本文前,建议阅读下面的文章,做好知识储备,方便本文的理解。

https://blog.csdn.net/rzleilei/article/details/128452528

一.Service类型ANR如何触发?

1.1 Service类型ANR的触发点

首先,我们看一下service类型的ANR触发点在哪里。

之前讲过,所有类型的ANR,最终都会通知到ANRHelper这个类的appNotResponding方法,service类型的自然也不例外。

所以最终的触发点在ActiveService类的serviceTimeout方法中:

void serviceTimeout(ProcessRecord proc) {
    ...
    anrMessage = "executing service " + timeout.shortInstanceName;
    ...
    if (anrMessage != null) {
            mAm.mAnrHelper.appNotResponding(proc, anrMessage);
    }
}

1.2 Service类型ANR的触发流程

serviceTimeout的调用流程其实也是很简单的,其核心就是一个延时消息机制。

Active注册一个延时消息,然后继续后面的流程,通知APP去执行对应的流程,APP执行完成后通知回系统,进行对应的延时消息的取消。

如果APP侧因为各种原因超时,没有按照的通知回APP,则就会发生service类型的ANR。

 这里超市时间有两个配置,区分前台service还是后台service。

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;
        mAm.mHandler.sendMessageDelayed(msg, proc.mServices.shouldExecServicesFg()
                ? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT);
    }


// How long we wait for a service to finish executing.
    static final int SERVICE_TIMEOUT = 20 * 1000 * Build.HW_TIMEOUT_MULTIPLIER;

    // How long we wait for a service to finish executing.
    static final int SERVICE_BACKGROUND_TIMEOUT = SERVICE_TIMEOUT * 10;

前台的超时时间为20S,后台为200S。

1.3 何时触发超时检查流程

整个Service启动流程中,在APP侧其实有多个生命周期的回调,每一个都有可能会超时,所以自然的,整个启动流程中,超时检查的机制并不止一次。

首先,在通知APP侧去创建Service时,会触发第一次的超时检查。

具体方法在realStartServiceLocked中,如下:

private void realStartServiceLocked(...) throws RemoteException {
    //第一次超时检查
     bumpServiceExecutingLocked(r, execInFg, "create", null /* oomAdjReason */);
    //通知APP去创建,创建完成后APP会通知回系统侧取消注册超时消息
    thread.scheduleCreateService(r, r.serviceInfo,
                    mAm.compatibilityInfoForPackage(r.serviceInfo.applicationInfo),
                    app.mState.getReportedProcState());
    //第二次超时检查
     sendServiceArgsLocked(r, execInFg, true);
}

其次,通知APP侧执行onStartCommand流程时,也会触发一次超时检查。

private final void sendServiceArgsLocked(ServiceRecord r, boolean execInFg,
            boolean oomAdjusted) throws TransactionTooLargeException {
    //第二次超时检查
    bumpServiceExecutingLocked(r, execInFg, "start", null /* oomAdjReason */);
    //通知到APP一侧
    r.app.getThread().scheduleServiceArgs(r, slice);
}

也就是说,系统侧在通知APP侧之前,会提前发送一个延时消息。如果APP正常完成了流程,则会通知回系统侧结束掉这个延时的消息,就不会触发ANR的流程了。

我们接下来,就看下APP侧收到通知后的流程。

二.APP侧如何处理的

2.1 service创建的回调

之前的文章介绍过,APP侧接受创建通知的是ApplicationThread对象中的scheduleCreateService方法,最终交给ActivityThread中的handleCreateService方法来处理。

private void handleCreateService(CreateServiceData data) {
    
    //完成创建service
    service = packageInfo.getAppFactory()
                    .instantiateService(cl, data.info.name, data.intent);

    //调用service的attach方法
    service.attach(context, this, data.info.name, data.token, app,
                    ActivityManager.getService());

    //调用service的onCreate方法
    service.onCreate();

    //通知回系统侧
    ActivityManager.getService().serviceDoneExecuting(
                        data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
}

最终,通过serviceDoneExecuting方法通知回系统侧。serviceDoneExecuting方法具体如何执行取消注册流程的,我们2.3来讲。

2.2 service中onStartCommand生命周期的回调

APP侧最终处理scheduleServiceArgs的方法是ActivityThread中的handleServiceArgs方法:

private void handleServiceArgs(ServiceArgsData data) {
        CreateServiceData createData = mServicesData.get(data.token);
        Service s = mServices.get(data.token);
        if (s != null) {
            try {
                ...
                int res;
                if (!data.taskRemoved) {
                    res = s.onStartCommand(data.args, data.flags, data.startId);
                } else {
                    s.onTaskRemoved(data.args);
                    res = Service.START_TASK_REMOVED_COMPLETE;
                }

                ...
                try {
                    ActivityManager.getService().serviceDoneExecuting(
                            data.token, SERVICE_DONE_EXECUTING_START, data.startId, res);
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
            } catch (Exception e) {
                if (!mInstrumentation.onException(s, e)) {
                    throw new RuntimeException(
                            "Unable to start service " + s
                            + " with " + data.args + ": " + e.toString(), e);
                }
            }
        }
    }

我们可以看到,调用了service的onStartCommand方法之后,也调用了serviceDoneExecuting方法进行了回调通知。

2.3 serviceDoneExecuting回调通知

我们看下ActivityManagerService中的serviceDoneExecuting方法:

public void serviceDoneExecuting(IBinder token, int type, int startId, int res) {
        synchronized(this) {
            if (!(token instanceof ServiceRecord)) {
                Slog.e(TAG, "serviceDoneExecuting: Invalid service token=" + token);
                throw new IllegalArgumentException("Invalid service token");
            }
            mServices.serviceDoneExecutingLocked((ServiceRecord) token, type, startId, res, false);
        }
    }

完全委托给ActiveService来处理的。

serviceDoneExecutingLocked中的逻辑有些复杂,我这精简一下,只保留最核心的。

就是取消超时类型消息注册的代码,如下:

private void serviceDoneExecutingLocked(ServiceRecord r, boolean inDestroying,
            boolean finishing, boolean enqueueOomAdj) {
       ...
        mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_TIMEOUT_MSG, r.app);
       ...
    }

三.Service类型ANR实例

按照上面的分析,我们知道有两种场景都会导致service类型的ANR触发。

分别是:

1.service创建流程

2.service执行onStartCommand生命周期流程。

当然这两步只是流程,具体导致的原因又可能会有很多了。如果service是首次创建的话,那么还会创建Application,Application创建耗时一样会导致Service类型的ANR事件。

我们这里做两个例子,验证下我们的猜测:

例子1:service的onCreate中进行耗时操作

代码如下:

public class ThreadService extends IntentService {
    @Override
    public void onCreate() {
        super.onCreate();
        Log.i("ThreadService", "ThreadService onCreate");
        try {
            Thread.sleep(60_000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

然后通过代码启动:

 val intent = Intent(this, ThreadService::class.java)
 startService(intent)

这里使用的是startService方法,而不是startForegroundService,如果后台启动的话,就需要休眠200S以上了。

最终实验结果,果然产生了service类型的ANR。

例子2:service的onStartCommand中进行耗时操作

代码如下:

public class ThreadService extends IntentService {
    @Override
    public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
        Log.i("ThreadService", "onStartCommand");
        try {
            Thread.sleep(60_000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return super.onStartCommand(intent, flags, startId);
    }
}

同样启动service,果然也发现出现了Service类型的ANR。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/128114.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

在线文档查看器:Gleamtech Document Viewer 6.6.1

DocumentUltimate Document Viewer and Converter for ASP.NET Core, MVC 和 WebForms 查看几乎任何文档类型(70 多种文件格式,包括 PDF 和 Microsoft Office)。 HTML5 零足迹查看器。在文档类型之间转换。 特征 适用于桌面和移动浏览器的通…

【XR】为挑战性环境优化6DoF控制器追踪

Oculus分享:为挑战性环境优化6DoF控制器追踪 尤其是针对具有挑战性的追踪环境 映维网曾在九月和十一月分享了关于Oculus Constellation追踪系统的相关细节,其中负责AR/VR设备输入追踪的Facebook工程经理安德鲁梅利姆(Andrew Melim&#xff…

舆情监测平台都有哪些,舆情监测平台使用工作总结

舆情监测平台(public opinion monitoring platform)是一种用于监测和分析网络上的舆论动态的工具。这些平台通常通过爬取网络上的新闻、博客、论坛、社交媒体等信息来收集数据,并使用自然语言处理技术和数据挖掘技术来分析数据。舆情监测平台常常被用于政府、企业、…

牛客竞赛每日俩题 - Day12

目录 set的插入删除 vector<string>的应用 set的插入删除 数据库连接池__牛客网 [解题思路] 循环接收每组用例&#xff0c;对于每组用例进行如下操作&#xff1a; 1. 依次获取每个状态&#xff0c;如果该状态是"connect"&#xff0c;则将其id插入到set中&…

Adaboost模型的python实现

文章目录介绍Adaboost库参数介绍实例二分类问题多分类问题作者&#xff1a;李雪茸介绍 Adaboost算法是一种集成学习(ensemble learning)方法。在集成学习中&#xff0c;强学习器指的是由多个机器学习模型组合形成的精度更高的模型。而参与组合的模型就被称为是弱学习器。进行预…

数据治理:数据治理之道-数据战略

参考《一本书讲透数据治理》、《数据治理》等 文章目录数据战略定义DAMA对数据战略的定义DCMM对数据战略的定义《一本书讲透数据治理》对数据战略理解数据战略与企业战略、数据架构的关系数据战略的3个要素战略定位短期目标中期目标长期目标实施策略行动计划实施数据战略的5个步…

NFT存储使用NFTUp上传(NFT.Storage)

文章目录NFT存储使用NFTUp上传(NFT.Storage)NFT.StorageNFTUp安装和使用NFT存储使用NFTUp上传(NFT.Storage) NFT.Storage 官网&#xff1a;https://nft.storage/ NFT.Storage&#xff0c;它可以让用户免费使用IPFS与Filecoin存储NFT及元数据内容。 NFT.Storage&#xff0c;…

信息系统业务安全服务资质

信息系统业务安全服务能力评定是指依据《信息化建设企业信息系统业务安全服务能力评定标准》&#xff0c;对信息化建设企业信息系统业务安全服务能力的符合性评价&#xff0c;包括综合条件、财务状况、业绩要求、管理能力、技术实力、人才保障六个方面。通过评定&#xff0c;可…

小黑正在苦于hbase的rowkey的日常积累:md5字符串加密

import hashlib content hello md5hash hashlib.md5(content.encode(utf-8)) md5 md5hash.hexdigest() print(md5)5d41402abc4b2a76b9719d911017c592 参考链接&#xff1a; https://wenku.baidu.com/view/bceba829f22d2af90242a8956bec0975f465a46c?frsogou&wkts167237…

电脑为什么这么卡?6个方法处理电脑卡顿

你是否打开电脑就卡到不行&#xff1f;电脑的开机速度慢&#xff0c;就连打开网页也在转圈圈&#xff0c;一直加载不出来。世界上最痛苦的事莫过于此&#xff0c;想要好好工作&#xff0c;却一直加载不出网页。你知道电脑为什么这么卡吗&#xff1f;其实大多数的原因都在这篇文…

借助 Material Design,帮助您打造更好的无障碍应用 (下篇)

随着时代的发展&#xff0c;"无障碍体验" 对开发者的意义也愈发重大&#xff0c;在上一篇文章中&#xff0c;我们为您介绍了无障碍布局和排版、文案等相关内容。本文将进一步为您介绍图片、声音和运动、实现无障碍的内容。图片图片类型要知道何时以及如何使图片遵循无…

多维尺度MDS案例分析

多维尺度&#xff08;multidimensional scaling, MDS&#xff09;&#xff0c;是一种将研究对象之间距离或者不相似度的直观展示&#xff0c;较为典型的研究对象是地理位置&#xff0c;当然也可以是观点、颜色等任意各类实体或抽象概念&#xff0c;比如茶的口味不相似情况。多维…

MoCo论文:Momentum Contrast for Unsupervised Visual Representation Learning

目录一. 引言二. 背景介绍&#xff1a;对比学习三. 标题和作者四. 动量方式&#xff1a;五. 摘要六. 相关工作七. 结论八. MoCo方法九. MoCo伪代码十. 文章贡献10.1 第一个贡献&#xff1a;如何把一个字典看成队列10.2 文章的第二个贡献&#xff1a;如何使用动量的思想去更新编…

GO-生命周期

好奇心 出于好奇&#xff0c;想了解go的执行生命周期&#xff0c;于是尝试跟着go的源码看看执行过程 go源码地址&#xff1a;GitHub - golang/go: The Go programming language 1.根据命令行编译文件&#xff0c;然后执行二进制文件 &#xff08;1&#xff09;从go运行命令…

基于深度学习的人工林地面激光扫描点云立木特征参数提取方法

Abstract 利用基于三维点云的技术量化立木和立木参数&#xff0c;可以在林业生态效益评估和立木培育和利用中发挥关键作用。随着光探测与测距&#xff08;LiDAR&#xff09;扫描等三维信息获取技术的进步&#xff0c;可以更高效地获取大面积、复杂地形的树木林分信息。然而&am…

IFC常用关系定义

IFC常用关系定义 IfcRelDefinesByType IfcRelDefinesByType表示对象化的关系(The objectified relationship)&#xff0c;定义了一种对象类型定义(object type)和对象实体(object occurrences)的关系。IfcRelDefinesByType是1:n的关系&#xff0c;可以将一个对象类型定义(obj…

深入浅出scala之集合体系(集合即容器)(P46-4)

文章目录一、容器概念二、定长数组一、容器概念 1.集合是一种用来存储各种对象和数据的容器。 2.Scala集合分为可变和不可变的集合&#xff0c;不可变集合可以安全的并发访问。 可变集合可以在适当的地方被更新或扩展。这意味着可以修改&#xff0c;添加&#xff0c;移除一个集…

笔记:软件工程常用开源文档模板 + 软件著作权

https://github.com/AlexanderZhou01/China-software-copyright 下载以上的工程 解压放到U盘里 打开 D:\China-software-copyright-master 国家版权保护中心网址 办理步骤为企业获取政策优惠&#xff0c;减免。为学生提供成果。 办理步骤 (3030)∗50(3030)*50(3030)∗5…

Python 高效率传参调用 MATLAB 程序

Background python调用matlab脚本需要注意前置条件&#xff0c;具体参考官方文档&#xff1a;从 Python 中调用 MATLAB&#xff0c;大致就是两点&#xff1a;一是需要python和matlab版本对应&#xff0c;二是需要matlab运行环境mcr。具体安装配置可以参考&#xff1a;java和pyt…

使用Filebeat和AWS CloudWatch Logs将EC2上的Tomcat的access_log传送到ELK

文章目录背景和方案选择前提注册AWS账号创建EC2实例注意事项在EC2实例上安装aws-cloudwatch-agent注意事项测试aws-cloudwatch-agent是否可用使用Docker Compose部署ELK使用Docker Compose部署Filebeat配置文件说明docker-compose.yml说明filebeat配置文件说明input配置AWS Clo…