当前位置: 首页 > news >正文

NativeCrash 率从万分位降到十万分位,我做了这几件事...

作者:郑丹

NativeCrash率从万分位降到十万分位,我做了这几件事...

一、背景

在 App 开发过程中,崩溃率是衡量 App 稳定性的关键指标。因为 App 崩溃不仅仅影响用户的即时体验,更对用户留存率构成了潜在的威胁。它如同一颗隐形的定时炸弹,随时可能引发用户体验的灾难。App 崩溃分为 Java Crash 和 Native Crash 2 种。

Native Crash 因为其上下文信息不完整、错误提示模糊以及难以捕捉等特性,相比较 Java Crash,定位及修复更为棘手困难。作为外卖商家日常运营不可或缺的助手,饿了么商家版 APP 的用户体验一直是我们团队的首要考量。然而,伴随着业务迭代与设备系统的不断升级,我们的应用 Native Crash 率上升明显,一度从原先的万 2 攀升到了万 6 以上。为此我们启动了 Native Crash 的专项治理行动,致力于为用户提供一个更加可靠稳定及高效的 App 应用,让所有商家都能享受到无忧的数字化运营体验。



(治理前 crash 趋势表)

二、Native Crash 成因及描述说明





为了更好的治理 Native Crash,先来了解下 Native Crash 的成因。从上图可以看到,发生在 Java 层的 Crash 称为 Java Crash,该 Crash 异常日志有具体的堆栈信息可以查看分析,修复相对明确具体。发生在 Native 层的 Crash 称为 Native Crash,由于大多该类型 crash 堆栈信息模糊,导致定位耗时,成效低。

常见导致 Native Crash 的原因有如下几种类型:

1. jni 内部数组越界、缓冲区溢出、空指针、野指针等;

2. jni 中多线程出现竞争,比如一个线程调用 jni 接口释放了内部一个指针,另一个线程调用另外一个 jni 接口还在使用这个指针;

3. Android ART 发现或出现异常;

4. 其他 framework、Kernel 或硬件 bug。

但是在 Native Crash 上报异常日志中均会包含异常的信号量及信号 Code 码,故了解信号量及信号 Code 有助于我们加快确定排查方向。

(异常描述表)

三、准备工作

当前我们使用的 crash 收集平台和阿里集团一致,都使用 EMAS 平台来收集和分析应用的 Crash 异常堆栈信息,该平台功能和市面上常用的友盟、bugly 等应用监控平台功能类似,提供 crash 数量、关键异常信息及异常堆栈内容等。同时结合 UC LogAnalyzer 分析工具(主要作用分析 UC webview 相关异常) 来协助排查问题提升处理效率。





四、APP 异常整体分析

为了更好的分析 Native Crash,我们以 2024 年度某个时间点 top crash 聚合数据为例来展开分析。

针对上述 top crash 特征分析提取内容如下:

五、案例 Case 说明

5.1 libart.so 异常

在分析 top1 libart.so 问题时,解析相关日志发现问题大多发生在 APP 初始化阶段。以某个 crash 日志为例,发生问题的进程 id=24657,线程 id=17891。





查看日志中与线程活动相关的记录,注意到 tag 为“XMPush-24657”的异常日志,初步判断可能为小米推送服务初始化异常导致。从前面该异常特征分析看 99%以上都集中在小米和红米设备上,和小米推送刚好能关联上。但是分析小米推送 jar 包代码及和集团多个采用相同版本小米推送服务的团队交流确认,他们并未遇到类似问题。和小米技术同学对接,对方也未接收到该版本异常反馈。故先暂时否定小米推送引发异常问题推论。

为了获取更多信息,只能再次分析日志。捞取数十份日志后,在某个日志中发现发生异常时主线程信息。





经深入探究,发现 App 启动过程中某个特定场景需要在初始化阶段启动一个独立线程对 SharePreferences 文件处理后存储操作,而该处理过程存在潜在风险,可能诱发不可预期的崩溃问题。因此尝试在灰度版本改用文件来处理。新版本上线后该 crash 清零。

5.2 libjsi.so 异常

从日志上看 crash 信号量为 SIGSEGV,code = SEGV_MAPPER 。从前面章节的异常描述表中可知 SEGV_MAPPER 异常为空指针异常。翻看日志找到发生异常的调用链堆栈如下。

jsValue = callbackFunction.call(jsContext, null, returnJSValue);

从上面代码分析如果异常确实是空指针造成,那么就有 2 种可能:

  • 外部传入的参数为空;

  • so c++代码变量为空。

其中 异常的 libjsi.so 由外部三方提供,遵循优先排查自身可控因素的原则,我们先来排查调用方法的三个参数,jsContext 成了引发问题的最大疑点。为了验证这一假设,我们将 jsContext = null 强制赋值作为参数进行验证,复现了该异常,其中异常堆栈和错误信息与用户上报的内容吻合。确定了原因后通过对参数判空来解决。

// 如果被回收了就不执行if (jsContext != null && jsContext.isDisposed()) {return null;}jsValue = callbackFunction.call(jsContext, null, returnJSValue);

5.3 arm-v7a OOM 异常





上述日志显示在分配内存时发生异常 ( Adreno-GSL: <gsl_memory_alloc_pure:2955>: ERROR: kgsl_sharedmem_alloc() failed! Allocation size: (10200 KB); Flags: (0x100000) ),out of memory 了。同时结合查看发生异常的设备特征均为 32 位 so (CPU 型号: armeabi-v7a)



推断低版本设备在申请分配内存时异常,参考《阿里开源 Patrons:大型 32 位 Android 应用稳定性提升 50% 的“黑科技”》文章引入 Patrons 库来解决提升低端设备性能。

5.4 #00 pc 0xda58 [anon:.bss]

异常报错不局限于上述的 anon:.bss,也可能是 libwebviewuc.so 或者关联了其他 so 报错。但是日志中有下面特征:

  • app 启动时间较短,一般都在几秒内;

  • 相关线程名为 ANRHandler ,并且日志中对应 tag: ANRHandler 条目中包含类似 app=cn.chuci.and.wkfenshen 等对应多开包名信息。

或者搜索异常日志,可以发现[anon:.bss]日志下真实包名前面为多开软件的包名信息:

根据上述 app 包名查询了解到我们应用的启动源于多开软件的使用,通过回访部分用户确认是在该时间点通过多开软件打开了相关的 app,但是用户并未感知到崩溃异常。初步判断,在多开环境下,应用在初始化阶段可能遭遇了某些异常情况,导致崩溃并上报异常。同时多开环境自动重启应用,从而使得用户未感知到崩溃现象,只觉得是打开变慢了。

针对多开异常,我们考虑两种解决方案思路:

一是 app 初始化时检测是否处于多开环境,如果是则限制应用初始化。如何检测线上已有对应方式;

二是将多开环境下的崩溃问题作为普通崩溃异常解决。从用户体验和业务安全的角度考虑,允许用户合理多开是更优的选择。

然而,鉴于多开环境的复杂性和多样性,全面解决这一问题将需要投入大量的人力资源。鉴于当前此类崩溃事件数量较少,占比较低(1%以下),我们的策略是暂时保持观察状态,持续监控该异常情况,以便在必要时采取进一步行动。

六、结果

最终通过团队成员的通力合作,我们成功地将 App 的 Native Crash 率优化至历史低位——仅 0.007%。这个结果不仅提升了 app 的稳定性,更获得了用户的一致好评与积极反馈。我们后续将持续关注并进一步优化产品性能,为用户提供更加稳定、流畅的应用体验。



(治理后 crash 趋势表)

参考链接

[01] 《阿里开源 Patrons:大型 32 位 Android 应用稳定性提升 50% 的“黑科技”》

阿里开源 Patrons:大型 32 位 Android 应用稳定性提升50%的“黑科技”_语言 & 开发_刘志龙_InfoQ精选文章

[02] 《android 多开原理和检测》

https://blog.csdn.net/c_kongfei/article/details/119914810


http://www.mrgr.cn/news/58349.html

相关文章:

  • 文件搜索利器 Everything v1.4.1.1024 官方正式版
  • 部署Leanote 蚂蚁笔记
  • 【Unity】仓库逻辑:拾取物体进仓库和扔掉物品
  • 对话系统介绍
  • REQUIRED、REQUIRES_NEW、NESTED的区别
  • yarn的安装与使用以及与npm的区别(安装过程中可能会遇到的问题)
  • 对比两个el-table,差异数据突显标记
  • springboot仓库管理系统-计算机毕业设计源码19585
  • 集群分发脚本
  • WUP-MY-POS-PRINTER 旻佑热敏打印机票据打印uniapp插件使用说明
  • 被面试官怼了,对nacos的原理都不理解,还多年的微服务工作经验?
  • CTF-RE 从0到N 1-1-1 开始之前-c函数手册
  • 一年四起供应链投毒事件的幕后黑手
  • 储能蓝海:技术革新与成本骤降引爆市场
  • python_删除二维列表的制定列
  • 商汤科技裁员动真格,战略转型组织大变革
  • 基于STM32设计的智能鱼缸(蓝牙版)
  • Vue Router 如何配置 404 页面?
  • GFF: Gated Fully Fusion for Semantic Segmentation门控融合语义分割-论文阅读笔记
  • 华为OD机试 - 字符串最后一个单词的长度(Java 2024 E卷 100分)
  • web 应用层接口请求日志
  • 面试题:JVM(一)
  • DLL修复工具 v4.2.0.40217 免安装一键修复
  • linux下gpio模拟spi三线时序
  • opencv - py_imgproc - py_geometric_transformations 几何变换
  • 设计师的新宠:7款不容错过的界面设计软件