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

news2025/1/11 3:43:22

系列文章目录

提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加
例如:第一章 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获取不到与应该相关堆栈时,可参考本篇思路进行分析。

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

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

相关文章

数据压缩新利器!小精灵ELF助你高效存储与传输

存储空间不够用&#xff1f;网络传输太慢&#xff1f;想必每个人在生活中都会遇到这些问题。看着爆满的硬盘、焦急的等待数据的接受&#xff0c;更新设备&#xff1f;不是每个人都能承担这个成本。那不如尝试一下无损压缩&#xff1f; 为了减少存储空间的占用&#xff0c;提高…

《Netty》从零开始学netty源码(五十七)之ServerBootstrap.bind()

目录 ServerBootstrap.bind()initAndRegister()init()register()doBind0() ServerBootstrap.bind() 在第一篇的HelloWorld中通过ServerBootstrap.bind()方法绑定端口号并最终启动Netty的服务&#xff0c;服务端的bind过程如下&#xff1a; 上面的代码主要分成两部分&#xff0…

【P20】JMeter XPath提取器(XPath Extractor)

文章目录 一、准备工作二、测试计划 一、准备工作 百度&#xff1a;https://www.w3school.com.cn/example/xmle/cd_catalog.xml 进入网页后&#xff0c;右键检查或按F12&#xff0c;打开调试工具 如图&#xff0c;使用XPath提取器&#xff08;XPath Extractor&#xff09;获取…

typescript学习笔记(下)

1、类型拓宽 所有通过 let 或 var 定义的变量、函数的形参、对象的非只读属性&#xff0c;如果满足指定了初始值且未显式添加类型注解的条件&#xff0c;那么它们推断出来的类型就是指定的初始值字面量类型拓宽后的类型&#xff0c;这就是字面量类型拓宽。 下面我们通过字符串…

数据结构-排序-(选择、堆排序、归并排序、基数排序)

目录 一、选择排序 二、堆排序 排序 效率分析 三、归并排序 排序 分析 四、基数排序 一、选择排序 思想&#xff1a;每趟在待排序元素中选取关键字最小的元素加入有序子列 不稳定性 空间复杂度&#xff1a;O(1) 时间复杂度&#xff1a; void swap(int &a,int &…

[Linux] 动态 / 静态库的生成与使用

文章目录 简要概念 静态库生成使用 动态库生成使用 简要概念 库一般分为两种&#xff1a; 静态库动态库 在 Linux 中&#xff1a; 如果是动态库&#xff0c;库文件是以 .so 作后缀的如果是静态库&#xff0c;库文件是以 .a 作后缀的 库文件的命名&#xff1a; libXXX.so …

RBTree

目录 红黑树的概念 红黑树性质 红黑树节点设计 红黑树的插入 红黑树的验证 红黑树和AVL树的比较 红黑树的概念 红黑树&#xff0c;是一种二叉搜索树&#xff0c;但在每个结点上增加一个存储位表示结点的颜色&#xff0c;可以是Red或 Black。 通过对任何一条从根到叶子的…

Point-SLAM: Dense Neural Point Cloud-based SLAM阅读记录

前言 只读了前半部分就感慨文章结构真的好清晰&#xff0c;从Introduction到related work完完全全都在体现它的motivation——他做了一件什么事情&#xff1f;以及为什么要这么做&#xff1f;解决了什么问题。 第一遍阅读 keywords: 以RGBD作为输入 使用点云表示场景的 dens…

【P21】JMeter XPath2 提取器(XPath2 Extractor)

文章目录 一、准备工作二、测试计划 一、准备工作 百度&#xff1a;https://www.w3school.com.cn/example/xmle/cd_catalog.xml 进入网页后&#xff0c;右键检查或按F12&#xff0c;打开调试工具 如图&#xff0c;使用XPath2 提取器&#xff08;XPath2 Extractor&#xff09;…

python 使用pandas或xlrd、xlwt实现对Excel的读取、添加、追加等一系列封装

不说了&#xff0c;又是造轮子的一天。在此我要严重批评CSDN或百度一堆浑水摸鱼的&#xff0c;某些人明明代码明显报错也来上传发博客&#xff0c;要么就是标题党&#xff0c;代码没报错但压根就不是实现那个功能的&#xff0c;简直是浪费时间。 废话不多说直接贴代码&#xff…

Linux—网络基础

目录 计算机网络背景 网络发展 认识 "协议" 网络协议初识 协议分层 OSI七层模型 TCP/IP五层(或四层)模型 网络传输基本流程 协议报头 局域网通信 网络传输流程图 局域网通信图 跨网络通信图 数据包封装和分用 网络中的地址管理 认识IP地址 认识MAC地址…

8款主流产品原型设计软件分享

在产品设计中&#xff0c;你知道如何选择合适的产品设计软件吗&#xff1f;每个产品设计软件的功能实际上是不同的&#xff0c;不同的产品设计软件应用领域是不同的。 只有深入了解每个产品设计软件的功能和主要适合该软件的行业&#xff0c;我们才能在设计相应的产品时找到合…

linux内核篇-进程及其调度

介绍一个程序从源文件到进程执行的过程 1、编译链接&#xff08;源文件到二进制文件&#xff09; Linux 下面二进制的程序也要有严格的格式&#xff0c;称为ELF&#xff08;Executeable and Linkable Format&#xff0c;可执行与可链接格式&#xff09; &#xff0c;这个格式可…

Simulink 和 Gazebo联合仿真控制机械臂【Matlab R2022a】

逛 B 站&#xff0c;偶然发现一个 up 主上传的视频&#xff0c;可以实现 Simulink 中搭建机器人的控制器设计&#xff0c;对运行在虚拟机中 Gazebo 中的机械臂进行控制&#xff0c;链接&#xff1a;三关节机械臂Gazebo-Simulink联合仿真&#xff0c;这让我很感兴趣&#xff0c;…

Web基础 ( 一 ) HTML

1.HTML <input /><input typebutton value按钮 />1.1.概念 1.1.1.HTML文件是什么 HTML表示超文本标记语言&#xff08;Hyper Text Markup Language&#xff09;, HTML文件是一个包含标记的文本文件, 必须有htm标记或者html扩展名。 可以通过浏览器(Browser)直接…

如何用自己公司的知识、流程等来训练Chat GPT?

在玩过 ChatGPT 并向它询问有关世界、金融和初创公司的一般问题后&#xff0c;我开始思考&#xff1a;“如果我可以用我自己的初创公司甚至大型公司的所有流程、知识和商业经验来训练 AI 模型会怎样&#xff1f;企业&#xff1f;” 使用您自己公司的知识、流程等培训 ChatGPT …

华为OD机试 - 计算网络信号、信号强度( Python)

题目描述 网络信号经过传递会逐层衰减,且遇到阻隔物无法直接穿透,在此情况下需要计算某个位置的网络信号值。 注意:网络信号可以绕过阻隔物。 array[m][n] 的二维数组代表网格地图, array[i][j] = 0代表i行j列是空旷位置; array[i][j] = x(x为正整数)代表i行j列是信号源,…

Python实现哈里斯鹰优化算法(HHO)优化XGBoost回归模型(XGBRegressor算法)项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档视频讲解&#xff09;&#xff0c;如需数据代码文档视频讲解可以直接到文章最后获取。 1.项目背景 2019年Heidari等人提出哈里斯鹰优化算法(Harris Hawk Optimization, HHO)&#xff0c;该算法有较强的全…

【A*算法——清晰解析 算法逻辑——算法可以应用到哪些题目】例题1.第K短路

A*算法 A*算法是什么例题1. 第K短路题意解析 欢迎观看我的博客&#xff0c;如有问题交流&#xff0c;欢迎评论区留言&#xff0c;一定尽快回复&#xff01;&#xff08;大家可以去看我的专栏&#xff0c;是所有文章的目录&#xff09;   文章字体风格&#xff1a; 红色文字表示…

栈和队列的实现

栈 栈的概念 栈也是线性表的一种&#xff0c;但是栈只允许在固定的一端进行插入与删除数据&#xff0c;而进行插入与删除的一端同意称为栈顶&#xff0c;而另一端就称为栈底。简称&#xff1a;后进先出。 压栈&#xff08;push&#xff09;&#xff1a;将数据插入栈顶。 出…