ANR实战案例 - FCM拉活启动优化

系列文章目录

提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加
例如:第一章 Python 机器学习入门之pandas的使用


文章目录

  • 系列文章目录
  • 前言
  • 一、Trace日志分析
  • 二、业务分析
    • 1.Firebase源码分析
    • 2.Firebase官方查看
      • 官方文档
      • Demo中issue查看
  • 三、问题分析
    • 3.1 打点数据统计分析
    • 3.2 冷启动时间测试
    • 3.3 应用启动分析
    • 3.4 启动优化
    • 3.5 三方SDK初始化禁用效果
    • 3.6 ANR优化效果
    • 3.7 问题根治
    • 3.8 问题复盘
  • 总结


前言


一、Trace日志分析

如果您想降低 ANR 率,首先要做的是找出错误的原因。最直接的方法是尝试分析 Google Play 中排名靠前的 ANR 组。当我们检查控制台时,显示如下:
在这里插入图片描述
占比靠前的几乎每个组都有一个标题“ Broadcast of Intent { act=com.google.android.c2dm.intent.RECEIVE } … ”,包含该类型的ANR占比接近60%。Google Play 后台堆栈详情如下:

在这里插入图片描述

主线程堆栈:
在这里插入图片描述

从堆栈未找到该问题分析入口,于是在项目中搜索“ Broadcast of Intent { act=com.google.android.c2dm.intent.RECEIVE } … ”,得知该acttion为Firebase组件FCM发送通知拉活我们应用的广播。于是继续研究FCM内部实现。

二、业务分析

1.Firebase源码分析

搜索FirebaseSDK,发现“ Broadcast of Intent { act=com.google.android.c2dm.intent.RECEIVE } … ”是内部的一个静态注册广播,如下图所示:
在这里插入图片描述
该意图属于FirebaseInstanceIdReceiver广播,考虑是否广播这里出现了耗时?
在这里插入图片描述
查看其父类CloudMessagingReceiver中onMessageReceive调用方式:
在这里插入图片描述
可以看到onReceive方法内部虽然进行了混淆,但可以看到大概逻辑,是通过一个线程池中子线程进行处理返回的广播结果。好像处理的也没有毛病。

源码的这个方向没发现问题,继而换个思路查看官方文档及Demo。

2.Firebase官方查看

官方文档

通过源码查看,发现CloudMessagingReceiver属于messaging库。
官方文档地址:
https://firebase.google.com/support/release-notes/android#messaging_v23-0-7
在这里插入图片描述
messaging库升级为23.0.7后,Google play后台标题为“ Broadcast of Intent { act=com.google.android.c2dm.intent.RECEIVE } … ”类型的ANR比例没有明显下降。
然后查看Cloud Messaging更新记录,包括23.1.1等一共有5个版本致力于解决ANR,依次升级测试后仍然没有解决我们的问题。

Demo中issue查看

从官方提供Demo的issue查看,不少开发者也遇到了这个ANR:在这里插入图片描述

然后做了如下尝试:
https://github.com/firebase/firebase-android-sdk/issues/3990
在这里插入图片描述
参考issue-3990中描述的方法,将广告初始化移到Activity阶段,似乎依然没有缓解问题。

https://github.com/firebase/firebase-android-sdk/issues/3468
在这里插入图片描述
参考issues-3468将基础库降级,以及新的Bom方式配套引入,均没有解决问题。

三、问题分析

基于前面的途径都没有解决问题,于是我决定自己根据该问题现象进行深入研究。

3.1 打点数据统计分析

首先,我对Firebase后台发生ANR时间点的打点数据进行了统计分析,发现大部分集中在Application.onCreate 阶段。
这让我好像看到了一点曙光,于是向 Application.onCreate 添加人为延迟并检查不同的场景。发现如下:

  1. 当用户使用launcher app手动触发app launch时,Application.onCreate中的主线程阻塞,即使阻塞几分钟也不会报ANR
  2. 当使用广播接收器启动应用程序时,主线程阻塞时间少于 10 秒时不会报告 ANR。

3.2 冷启动时间测试

于是让测试帮忙找了线上ANR发生率比较高的具有代表性的机型,进行了冷启动时间测试,发现很多中低端机型的冷启动时间超过10s。
ps:由于业务主要是非洲国家,线上包含了大量的低端机及平均使用5-7年的手机。
于是我开始思考,是否启动时间跟该ANR具有相关性?

3.3 应用启动分析

最终利用kotlin的init特性获取了冷启动阶段的.trace文件

class App: MusicApplication() {
    init {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            Debug.startMethodTracingSampling("startup", 8 * 1024 * 1024, 100)
        }
    }

    override fun onCreate() {
        super.onCreate()
        Debug.stopMethodTracing()
    }
}

trace概览如下:
在这里插入图片描述
详细:
在这里插入图片描述
由函数调用耗时发现,启动阶段的大部分耗时都是因为三方库使用contentProviders调用初始化代码,在Provider阶段产生的耗时。
查看系统源码可知,
在这里插入图片描述
先执行完installContentProviders方法,才会执行到callApplicationOnCreate。于是接下来就想办法处理三方库的自动初始化。

3.4 启动优化

对相关三方SDK使用的Provider初始化进行禁用,使用tools:node=“remove”,示例如下:

<!--禁用FirebaseApp初始化-->
        <provider
            android:name="com.google.firebase.provider.FirebaseInitProvider"
            android:authorities="${applicationId}.firebaseinitprovider"
            android:exported="false"
            tools:node="remove"/>

        <!--FirebasePerformance初始化禁用 -->
        <provider
            android:authorities="${applicationId}.firebaseperfprovider"
            android:exported="false"
            android:initOrder="101"
            android:name="com.google.firebase.perf.provider.FirebasePerfProvider"
            tools:node="remove"/>

        <!--阻止令牌自动生成,防止Firebase Analytics及messaging自动初始化,二者需同时禁用-->
        <meta-data
            android:name="firebase_messaging_auto_init_enabled"
            android:value="false" />
        <meta-data
            android:name="firebase_analytics_collection_enabled"
            android:value="false" />

            

        <!--Google MobileAds广告SDK自动初始化禁用-->
        <provider
            android:name="com.google.android.gms.ads.MobileAdsInitProvider"
            android:authorities="${applicationId}.mobileadsinitprovider"
            android:exported="false"
            android:initOrder="100"
            tools:node="remove"/>



        <!--FaceBook 禁用 SDK 自动初始化功能-->
        <meta-data android:name="com.facebook.sdk.AutoInitEnabled"
            android:value="false"/>
        <provider
            android:name="com.facebook.internal.FacebookInitProvider"
            android:authorities="${applicationId}.FacebookInitProvider"
            android:exported="false"
            tools:node="remove"/>

禁用后在启动阶段异步进行手动调用。
由于使用到的三方SDK较多,上面只列举了部分SDK,还有其它:

  • 融云SDK的Provider初始化禁用,通过反射调用。
  • AutoSize库的Provider初始化禁用,通过AutoSize.checkAndInit调用。
  • 其它

3.5 三方SDK初始化禁用效果

三方SDK使用的Provider初始化禁用后,优化效果如下:
在这里插入图片描述
图1-三方SDK自动初始化优化前,k7机型测试,应用进程创建耗时4.04s 在这里插入图片描述
图2-三方SDK自动初始化优化后,k7机型测试,应用进程创建耗时0.12s

3.6 ANR优化效果

优化前Google play后台,“ Broadcast of Intent { act=com.google.android.c2dm.intent.RECEIVE } … ”类型ANR占比:
在这里插入图片描述
优化后Google play后台,“ Broadcast of Intent { act=com.google.android.c2dm.intent.RECEIVE } … ”类型ANR占比:
在这里插入图片描述
遗留的17.3%后面通过把FCM放到独立进程进行解决。

3.7 问题根治

FCM独立进程可参考:

<!--FCM独立进程 start-->
<service
    android:name="com.google.firebase.messaging.FirebaseMessagingService"
    android:directBootAware="true"
    android:exported="false"
    android:process=":light"
    tools:node="replace">

    <intent-filter android:priority="-500">
        <action android:name="com.google.firebase.MESSAGING_EVENT" />
    </intent-filter>
</service>

<receiver
    android:name="com.google.firebase.iid.FirebaseInstanceIdReceiver"
    android:exported="true"
    android:permission="com.google.android.c2dm.permission.SEND"
    android:process=":light"
    tools:node="replace">

    <intent-filter>
        <action android:name="com.google.android.c2dm.intent.RECEIVE" />
    </intent-filter>
</receiver>
<!--FCM独立进程 end-->

继承FirebaseInstanceIdReceiver的自定义类也得改为独立进程,否则收不到FCM推送消息。
然后通过跨进程广播传递FCM通知。

3.8 问题复盘

回顾第二小节的Firebase源码分析,已知Firebase的Messaging库内部是通过广播的形式来发送消息,实现业务App的拉活,查看常见ANR超时场景,前台广播的超时时间为10s,所以问题的根源还是应用被拉起的启动时间过久,导致该广播超时,从而产生了ANR。
在这里插入图片描述


总结

一般做海外业务的同学才会用到Firebase库,但解决问题的思路类似。当碰到此类疑难ANR问题,trace.txt获取不到与应该相关堆栈时,可参考本篇思路进行分析。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Python学习26:个人所得税计算器

描述‪‬‪‬‪‬‪‬‪‬‮‬‪‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‪‬‭‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‮‬‪‬‪‬‪‬‪‬‪‬‮‬‭‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‪‬‪‬‪‬‪‬‪‬‪‬‮‬‭‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‪‬ 2018年10月1日以前&#xff…

Elasticsearch:使用 count API 来获得所有文档的个数

在我开始使用 Elasticsearch 的时候&#xff0c;我希望获得给定查询的文档总数。比如我们想对数据进行分页显示。从 Elasticsearch 7.0之后&#xff0c;为了提高搜索的性能&#xff0c;在 hits 字段中返回的文档数有时不是最精确的数值。Elasticsearch 限制了最多的数值为10000…

截面空间计量模型(Stata)

截面空间计量模型(Stata) 文章目录 截面空间计量模型(Stata)[toc]1 广义空间自回归模型&#xff08;SAC&#xff09;2 空间误差模型(SEM)3 空间杜宾模型(SDM)4 广义空间嵌套模型(GNS)5 空间(自回归)滞后模型(SAR,SLM)6 空间杜宾误差模型(SDEM) 1 广义空间自回归模型&#xff08…

[ChatGPT] 从 GPT-3.5 到 GPT-5 的进化之路 | ChatGPT和程序员 : 协作 or 取代

⭐作者介绍&#xff1a;大二本科网络工程专业在读&#xff0c;持续学习Java&#xff0c;努力输出优质文章 ⭐作者主页&#xff1a;逐梦苍穹 ⭐如果觉得文章写的不错&#xff0c;欢迎点个关注一键三连&#x1f609;有写的不好的地方也欢迎指正&#xff0c;一同进步&#x1f601;…

考虑多能负荷不确定性的区域综合能源系统鲁棒规划(Python代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

Linux 虚拟机 磁盘扩容

概述 在单台虚拟机上部署了过多服务&#xff0c;导致磁盘使用过度达到98%。 现在扩容提高磁盘容量&#xff0c;增加10G。 现象 df -h df -ih du -s具体步骤 VMware 扩容 关闭虚拟机的情况下执行&#xff0c;类似于生产环境下需要关闭服务器&#xff0c;从而添加硬盘等相关操作…

sed命令的应用

sed命令的应用 一、sed编辑器sed的工作流程&#xff1a;sed的命令格式于常用选项命令格式常用选项常用操作&#xff1a; 三、实际操作打印内容删除行替换行数内容插入内容字符位置互换 一、sed编辑器 sed是一种流编辑器&#xff0c;流编辑器会在编辑器处理数据之前基于预先提供…

BlockChain-Account_TakeOver

题目描述 ECDSA 签名 假设我们的私钥为 d A d_A dA​而公钥为 Q A Q_A QA​&#xff0c; Q A d A ⋅ G Q_Ad_A\cdot G QA​dA​⋅G&#xff0c;接下来就是签名的过程&#xff0c;要签名的消息为 m m m 取 e H A S H ( m ) e HASH(m) eHASH(m)取 e e e的左边的 L n L_n L…

Ambari-2.7.7源码编译

0 说明 本文基于Ambari-2.7.7版本进行源码编译。所需的编译资料统一提供如下&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1F2D7zBGfKihxTBArnOilTw 提取码&#xff1a;8m17 1 前提条件 1.1 下载ambari源码包 wget https://github.com/apache/ambari/releases/t…

Linux:文本三剑客之sed编辑器

Linux&#xff1a;sed编辑器 一、sed1.1 sed编辑器1.2 sed编辑器的工作流程1.3 命令格式1.4常用选项1.5 常用操作1.6 实际应用 一、sed 1.1 sed编辑器 sed是一种流编辑器&#xff0c;流编辑器会在编辑器处理数据之前基于预先提供的一组规则来编辑数据流。sed编辑器可以根据命…

FE_Vue学习笔记 常用指令的学习【v-model filters v-text v-html v-cloak v-once v-pre 自定义指令】

1 收集表单数据 v-model 收集表单数据&#xff1a; 若&#xff1a;<input type"text">&#xff0c;则v-model收集的是value的值&#xff0c;用户输入的就是value值。 若&#xff1a;<input type"radio">&#xff0c;则v-modle收集的是value的…

浏览csdn博客自动隐藏侧边栏并只看目录

背景 CSDN 总算做了点好事&#xff0c;能够隐藏大部分无关信息&#xff0c;只看博客内容本身。具体如图&#xff0c;还在测试版 以我的一篇博客为例&#xff0c;原始界面&#xff0c;花里胡哨一堆 点击隐藏侧栏后的清爽版 点击只看目录后的清爽版 前提提要 安装油猴脚本&…

OLS样本估计量抽样分布模拟

OLS样本估计量抽样分布模拟 文章目录 OLS样本估计量抽样分布模拟1 OLS估计量分布2 R语言实现 1 OLS估计量分布 对于线性回归方程 Y β 0 β 1 X ε Y \beta_0\beta_1 X \varepsilon Yβ0​β1​Xε 利用普通最小二乘法(OLS&#xff09;估计上述方程参数使的假定(之一)是…

[译] Flutter 3.10 的新功能

[译] Flutter 3.10 的新功能 原文 https://medium.com/flutter/whats-new-in-flutter-3-10-b21db2c38c73 无缝的Web和移动端集成&#xff0c;Impeller稳定版的突破性图形性能&#xff0c;以及更多 欢迎使用Flutter 3.10&#xff01;我们非常期待展示我们令人惊叹的Flutter社区所…

示波器的数据处理怎么记录?

示波器的使用 - 记录和保存示波器测试结果 安泰测试为您分享如何记录示波器的数据。 "从您把示波器探头连接到器件的那一刻起&#xff0c;信号就开启了一次瞬间即可完成的重大旅程。它必须 跨过五个不同的“模块”&#xff0c;才能完成从器件到示波器&#xff0c;最后返回…

十五、Gateway网关

目录 Zuul网关和gateway网关的区别&#xff1a; Gateway路由配置 1、新建服务网关项目&#xff0c;并在项目pom文件中引入gateway网关依赖 2、在application.yml配置gateway 3、如果不用配置的方式配置gateway路由&#xff0c;还可以通过代码的形式配置 4、启动网关服务&…

Reed-Muller序列

Reed-Muller函数的由来 我们知道对于连续信号&#xff0c;时间和频率是对偶域(duality)&#xff0c;其中正弦函数是时移的特征函数&#xff08;where sinusoids are eigenfunctions of time shifts&#xff09;。而在汉明空间(Hamming space)中&#xff0c;there are discrete…

【软考|软件设计师】某计算机系统的CPU主频为2.8GHz

目录 题&#xff1a; CPI MIPS 题&#xff1a; 某计算机系统的CPU主频为2.8GHz。某应用程序包括3类指令&#xff0c;各类指令的CPI &#xff08;执行每条指令所需要的时钟周期&#xff09;及指令比例如下表所示。执行该应用程序时 的平均CPI为______&#xff1b; 运算速度…

ASP.NET Core 8 中身份验证的改进

ASP.NET Core 团队正在改进 .NET 8 中的身份验证、授权和身份管理(统称为“身份验证”)。新的 APIs 将使自定义用户登录和身份管理体验变得更加容易。新的端点将在没有外部依赖的单页应用程序(SPA)中启用基于令牌的身份验证和授权。我们还将改进我们的指引和文档&#xff0c;使…

基于SSM+JSP的人体健康信息管理系统

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…