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 添加人为延迟并检查不同的场景。发现如下:
- 当用户使用launcher app手动触发app launch时,Application.onCreate中的主线程阻塞,即使阻塞几分钟也不会报ANR
- 当使用广播接收器启动应用程序时,主线程阻塞时间少于 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初始化--><providerandroid:name="com.google.firebase.provider.FirebaseInitProvider"android:authorities="${applicationId}.firebaseinitprovider"android:exported="false"tools:node="remove"/><!--FirebasePerformance初始化禁用 --><providerandroid:authorities="${applicationId}.firebaseperfprovider"android:exported="false"android:initOrder="101"android:name="com.google.firebase.perf.provider.FirebasePerfProvider"tools:node="remove"/><!--阻止令牌自动生成,防止Firebase Analytics及messaging自动初始化,二者需同时禁用--><meta-dataandroid:name="firebase_messaging_auto_init_enabled"android:value="false" /><meta-dataandroid:name="firebase_analytics_collection_enabled"android:value="false" /><!--Google MobileAds广告SDK自动初始化禁用--><providerandroid: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"/><providerandroid: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-->
<serviceandroid: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><receiverandroid: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获取不到与应该相关堆栈时,可参考本篇思路进行分析。
相关文章:

ANR实战案例 - FCM拉活启动优化
系列文章目录 提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加 例如:第一章 Python 机器学习入门之pandas的使用 文章目录 系列文章目录前言一、Trace日志分析二、业务分析1.Firebase源码分析2.Firebase官方查看官方文档Dem…...

Kali-linux查看打开的端口
对一个大范围的网络或活跃的主机进行渗透测试,必须要了解这些主机上所打开的端口号。在Kali Linux中默认提供了Nmap和Zenmap两个扫描端口工具。为了访问目标系统中打开的TCP和UDP端口,本节将介绍Nmap和Zenmap工具的使用。 4.4.1 TCP端口扫描工具Nmap 使…...

判断浏览器是否支持webp图片
.WebP是谷歌主导的开放免费的网络图像格式,其核心编码来自VP8也就是同时支持WebP图片和WebM视频等。 这种图像格式追求的并不是无损画质,而是在有损画质的情况下尽可能的压缩图像体积但也尽量降低清晰度下降。 谷歌资助和发展该图像格式最主要的目的就是…...

【Qt编程之Widgets模块】-007:QTextStream类及QDataStream类
1 概述 QTextStream和QDataStream都是对流进行操作 QTextStream只能普通类型的流操作像QChar、QString、int…,其实就很类似我们c或者c中读写文件的感觉, QDataStream就厉害了,无论是QTextStream的普通类型的流操作还是一些特殊类型的流操作…...
js对map排序,后端返回有序的LinkedHashMap类型时前端获取后顺序依旧从小到大的解决方法
js对map排序,后端返回有序的LinkedHashMap类型时前端获取后顺序依旧从小到大的解决方法 js对map排序,后端返回有序的LinkedHashMap类型时前端获取后顺序依旧从小到大的解决方法 [{"2020": [{"id": 39,"createTime": &quo…...

JMX vs JFR:谁才是最强大的JVM监控利器?
大家好,我是小米!今天我们来聊一聊JVM监控系统,特别是关于JMX和JFR的使用。你是否有过在线上应用出现性能问题时,无法准确获取关键指标的困扰呢?那么,不妨听听我给大家带来的解决方案。 什么是JMX 首先&a…...
Laravel Collection 基本使用
创建集合 为了创建一个集合,可以将一个数组传入集合的构造器中,也可以创建一个空的集合,然后把元素写到集合中。Laravel 有collect()助手,这是最简单的,新建集合的方法。 $collection collect([1, 2, 3]);默认情况下…...
JUC并发编程19 | 读写锁
有一些关于锁的面试题: 你知道 Java 里面有哪些锁?读写锁的饥饿问题是什么?有没有比读写锁更快的锁?StampedLock知道嘛?(邮戳锁/票据锁)ReentrantReadWriteLock 有锁降级机制? Ree…...
springboot_maven项目怎么引入mybatis
在pom.xml文件中添加mybatis和mybatis-spring-boot-starter的依赖 org.mybatis mybatis ${mybatis.version} org.mybatis.spring.boot mybatis-spring-boot-starter ${mybatis.spring.version} 配置mybatis 在application.properties(或application.yml࿰…...

JAVA8的新特性——lambda表达式
JAVA8的新特性——lambda表达式 此处,我们首先对于Java8的一些特性作为一个简单介绍 Java 8是Java编程语言的一个重要版本,于2014年发布。Java 8引入了许多新特性和改进,以提高开发效率和性能。以下是Java 8的一些主要新特性: Lam…...

算法修炼之练气篇——练气六层
博主:命运之光 专栏:算法修炼之练气篇 前言:每天练习五道题,炼气篇大概会练习200道题左右,题目有C语言网上的题,也有洛谷上面的题,题目简单适合新手入门。(代码都是命运之光自己写的…...

利用GPU并行计算beta-NTI,大幅减少群落构建计算时间
1 先说效果 18个样本,抽平到8500条序列,4344个OTUs,计算beta-NTI共花费时间如下。如果更好的显卡,更大的数据量,节约的时间应该更加可观。 GPU(GTX1050):1分20秒 iCAMP包 的bNTIn.p(…...

Shiro框架漏洞分析与复现
Shiro简介 Apache Shiro是一款开源安全框架,提供身份验证、授权、密码学和会话管理。Shiro框架直观、易用,同时也能提供健壮的安全性,可以快速轻松地保护任何应用程序——从最小的移动应用程序到最大的 Web 和企业应用程序。 1、Shiro反序列…...

(数字图像处理MATLAB+Python)第七章图像锐化-第一、二节:图像锐化概述和微分算子
文章目录 一:图像边缘分析二:一阶微分算子(1)梯度算子A:定义B:边缘检测C:示例D:程序 (2)Robert算子A:定义B:示例C:程序 &a…...

C# | 内存池
内存池 文章目录 内存池前言什么是内存池内存池的优点内存池的缺点 实现思路示例代码结束语 前言 在上一篇文章中,我们介绍了对象池的概念和实现方式。对象池通过重复利用对象,避免了频繁地创建和销毁对象,提高了系统的性能和稳定性。 今天我…...
程序设计入门——C语言2023年5月10日
程序设计入门——C语言 1、window下安装gcc 课程来源:链接: 浙江大学 翁恺 程序设计入门——C语言 学习日期:2023年5月10日 1、window下安装gcc 如果想让gcc在windows下运行,需要将gcc,及对于的lib包,都安装到window…...
【2023华为OD笔试必会25题--C语言版】《03 单入口空闲区域》——递归、数组、DFS
本专栏收录了华为OD 2022 Q4和2023Q1笔试题目,100分类别中的出现频率最高(至少出现100次)的25道,每篇文章包括原始题目 和 我亲自编写并在Visual Studio中运行成功的C语言代码。 仅供参考、启发使用,切不可照搬、照抄,查重倒是可以过,但后面的技术面试还是会暴露的。✨✨…...

Grafana安装、升级与备份(02)
一、安装Grafana软件包 Grafana部署非常简单,直接使用yum命令从官网拉到安装再启动就可以了,本次使用的grafana版本为9.5.0 官网下载地址:Download Grafana | Grafana Labs # wget yum install -y https://dl.grafana.com/oss/release/grafana-9.5.0-1.x86_64.rpm # yum …...
【2023华为OD笔试必会25题--C语言版】《10 相同数字的积木游戏》——数组
本专栏收录了华为OD 2022 Q4和2023Q1笔试题目,100分类别中的出现频率最高(至少出现100次)的25道,每篇文章包括原始题目 和 我亲自编写并在Visual Studio中运行成功的C语言代码。 仅供参考、启发使用,切不可照搬、照抄,查重倒是可以过,但后面的技术面试还是会暴露的。✨✨…...

awk命令编辑
awk工作原理 逐行读取文本,默认以空格或tab键分隔符进行分隔,将分隔所得的各个字段保存到内建变量中,并按模式或者条件执行编辑命令。 sed命令常用于一整行的处理,而awk比较倾向于将一行分成多个“字段”然后再进行处理。awk信息…...
零门槛NAS搭建:WinNAS如何让普通电脑秒变私有云?
一、核心优势:专为Windows用户设计的极简NAS WinNAS由深圳耘想存储科技开发,是一款收费低廉但功能全面的Windows NAS工具,主打“无学习成本部署” 。与其他NAS软件相比,其优势在于: 无需硬件改造:将任意W…...

7.4.分块查找
一.分块查找的算法思想: 1.实例: 以上述图片的顺序表为例, 该顺序表的数据元素从整体来看是乱序的,但如果把这些数据元素分成一块一块的小区间, 第一个区间[0,1]索引上的数据元素都是小于等于10的, 第二…...

stm32G473的flash模式是单bank还是双bank?
今天突然有人stm32G473的flash模式是单bank还是双bank?由于时间太久,我真忘记了。搜搜发现,还真有人和我一样。见下面的链接:https://shequ.stmicroelectronics.cn/forum.php?modviewthread&tid644563 根据STM32G4系列参考手…...

srs linux
下载编译运行 git clone https:///ossrs/srs.git ./configure --h265on make 编译完成后即可启动SRS # 启动 ./objs/srs -c conf/srs.conf # 查看日志 tail -n 30 -f ./objs/srs.log 开放端口 默认RTMP接收推流端口是1935,SRS管理页面端口是8080,可…...
论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一)
宇树机器人多姿态起立控制强化学习框架论文解析 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一) 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化…...
Web 架构之 CDN 加速原理与落地实践
文章目录 一、思维导图二、正文内容(一)CDN 基础概念1. 定义2. 组成部分 (二)CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 (三)CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 …...

Springboot社区养老保险系统小程序
一、前言 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,社区养老保险系统小程序被用户普遍使用,为方…...

2025季度云服务器排行榜
在全球云服务器市场,各厂商的排名和地位并非一成不变,而是由其独特的优势、战略布局和市场适应性共同决定的。以下是根据2025年市场趋势,对主要云服务器厂商在排行榜中占据重要位置的原因和优势进行深度分析: 一、全球“三巨头”…...

Linux 内存管理实战精讲:核心原理与面试常考点全解析
Linux 内存管理实战精讲:核心原理与面试常考点全解析 Linux 内核内存管理是系统设计中最复杂但也最核心的模块之一。它不仅支撑着虚拟内存机制、物理内存分配、进程隔离与资源复用,还直接决定系统运行的性能与稳定性。无论你是嵌入式开发者、内核调试工…...
纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join
纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join 1、依赖1.1、依赖版本1.2、pom.xml 2、代码2.1、SqlSession 构造器2.2、MybatisPlus代码生成器2.3、获取 config.yml 配置2.3.1、config.yml2.3.2、项目配置类 2.4、ftl 模板2.4.1、…...