likes
comments
collection
share

Android 稳定性优化知识盘点

作者站长头像
站长
· 阅读数 50

对稳定性的理解

应用稳定性是最重要的性能指标之一,是APP质量构建体系中的基本盘,如果应用的稳定性出现问题,对产品、用户造成的伤害将是致命的。本文将从以下几个方面对应用稳定性优化进行整理。

需要说明,广义的稳定性不仅仅是崩溃问题,还包括卡顿、耗电、温度等指标,本文主要从崩溃率的角度进行学习。

  1. 稳定性常见指标分类:业界用来衡量稳定性的常见指标,稳定性优秀的衡量标准。
  2. 处理Crash的一般步骤:发生Crash时常规的处理步骤。
  3. 业务高可用性建设:在维护一整块业务时,为了防止出现稳定性问题,所采用的手段。
  4. 长效治理Crash:在更长的周期内如何防止稳定性劣化。
  5. 面试常见问题:面试中关于稳定性的常见问题。

稳定性常见指标分类

异常分类:Exception与ANR

Android的崩溃问题可以分为Exception、ANR两类。

  • Exception:应用内部代码发生异常
    • JE:Java Exception,在Java代码中发生未捕获的异常
    • NE:Native Exception,在Native代码中访问非法地址,程序主动abort等
  • ANR:应用无响应,不一定是代码逻辑导致的

JE、NE、ANR既可以独立统计,也可以汇总后作为总体崩溃率。

PV与UV

是衡量应用软件使用量的指标。

  • PV:Page View,指页面的全部展示量,不区分用户,如果一名用户打开了100次页面,PV=100
  • UV:Unique View,指对用户去重后的访问量统计,如果一名用户打开了100次页面,UV=1

因此,崩溃率在PV和UV两个维度上都可以进行统计。

  • PV Crash:评估问题影响严重程度
  • UV Crash:评估问题在用户群体内影响范围

增量崩溃率与存量崩溃率

  • 增量崩溃:由当前新增代码导致的崩溃,是导致大盘崩溃率发生波动的主要原因。要早发现早解决,避免带到线上成为存量崩溃。处理策略:重要且紧急
  • 存量崩溃:由存量代码引起,是需要持续跟进的问题,解决存量崩溃有助于拉低线上大盘的崩溃率。处理策略:重要不紧急

崩溃率评价指标

统计口径:

  • 分子JE+NE+ANR 发生总次数
  • 分母:PV

<2‰为合格,<1‰(万级)为优秀。

处理Crash的一般步骤

在处理Crash时,分为采集现场数据和分析崩溃原因两个步骤。

采集现场数据

能够复现的崩溃才是好崩溃

在处理崩溃时,采集现场信息是至关重要的,现场保留着很多有价值的线索,是我们进一步排查的风向标。

根据层级的不同,将现场信息分为:崩溃本身、运行时状态、系统状态三个层级。

一些成熟的崩溃采集平台(如bugly、Sentry)可以cover以下大部分内容,但应用开发者仍然需要上传用户操作日志等自定义信息。

崩溃本身

主要是崩溃堆栈和进程、线程信息。

  • 崩溃堆栈:这是最重要的信息,反映了崩溃时函数的调用栈。如果代码经过混淆,堆栈信息里的内容需要经过反混淆后才能进行分析,因此保存打包时的mapping文件至关重要
  • 进程与线程信息:崩溃的进程是哪一个,处于前台还是后台,线程是UI线程还是工作线程

运行时状态

系统状态

  • 系统硬件信息:CPU,ABI,内存总量,网络连接状态
  • 系统软件信息:Android版本、Linux内核版本、WebView内核版本,OEM软件版本,是否root,是否模拟器
  • 系统日志:Logcat、EventLog等

分析崩溃原因

这一步也就是解析Crash,在分析时有种仿佛化身为一名侦探,抽丝剥茧侦查犯罪现场,定位嫌疑人的感觉。

第一步:单点突破

针对单条崩溃日志进行细致分析。

  1. 确认严重程度:是会导致应用闪退,还是当前页面功能不可用,接口请求失败,又或者是用户根本无感知
  2. 确认优先级:根据严重程度确定处理优先级,优先级高的应当先处理
  3. 观察收集到的崩溃基本信息,注意那些Android版本兼容性导致的异常,可能在新版本不会有问题,但老版本Android就会崩溃。根据崩溃类型不同,浏览信息的侧重点有所不同:
    • JE:90%的异常通过堆栈可以找出调用关系,特别指出,对于OOM则要关注内存占用情况
    • NE:观察signal、code、fault addr等内容,崩溃信号signal的定义可见官方文档,其中SIGSEGV(空指针、非法指针)和SIGABRT(ANR、abort()调用)较为常见
    • ANR:先观察trace.txt文件,重点查看主线程状态、是否持有锁,IO信息和CPU占用信息,以及GC回收前后的状态
  4. 观察Logcat日志:尤其是Warning、Error级别的,对于ANR则需要搜索am_anr的关键字
  5. 资源使用情况:如内存(物理+虚拟)、文件句柄、线程数量

第二步:群体聚合

对于后台聚合完成的崩溃信息,查看它们有没有共性,可用于排查的共性有:

机型,Android版本,Rom版本,厂商,ABI,是否root,是否虚拟机,网络状态,当前打开的页面,进程状态,后台正在运行的服务等。尤其是Android系统版本,很容易出现由于版本变更导致原来可用的代码发生崩溃,或者一个功能只在高版本Android正常运行,在低版本则有概率崩溃。我曾经处理过一个例子是显示Toast时偶现BadTokenException,就是在8.0以下才会发生的。

稳定性长期治理

根据所处流程,采取不同的稳定性优化策略

开发阶段

  • 开发是质量的第一道关卡,问题发现得越早,修复成本就越低
  • 统一编码规范,增强编码安全教育,增强代码评审,进行结对编程等
  • 代码架构优化,对常用能力、底层功能进行模块封装与复用,设计单元测试,对于接口返回失败等情况进行统一的收口与错误处理

测试阶段

  • 新功能测试:本次新增功能,以及新增功能所影响到的部分
  • 主流程回归测试:核心流程
  • 覆盖安装测试:覆盖安装老版本过程中,缓存、数据库等应当兼容
  • 兼容性测试:不仅是本公司手机,还有安装应用到第三方手机的场景
  • 边界条件:如服务器宕机、返回数据异常、弱网和无网情况

代码合并阶段

  • 冲突处理:如果代码有冲突则优先处理冲突,尤其是依赖的第三方库,如果不同的分支引入了不同版本,在合入代码时应当确认最终版本能够满足不同分支的需求
  • 编译检查:处理完冲突后,打包安装并回归主流程,
  • 静态扫描:借助lint等工具进行代码静态扫描,发现潜在的风险点
  • 自动化测试:如果项目有集成Appium等自动化测试框架,则应当在合并代码后自动执行

发布阶段

  • 采取灰度策略,先在小范围小量级内投放新版本升级,逐步扩大升级覆盖的用户范围,当灰度版本的稳定性、业务数据等指标合格后(不低于大盘5%),才进行全量发布。进行多轮灰度,并且可以根据特定机型、OS版本进行专门灰度,防止特定条件下的问题发生
  • 采取ABTest策略:灰度时通过发布一个新功能包+一个对比包,来进行稳定性、业务数据的比对。之所以不采用线上全量版本作为对比包,是因为两者量级不同,在一些数据上的表现会有差异。而ABTest则通过控制两者具有相同的量级,防止这方面差异的产生。同时,可以在指定的系统版本、机型、用户群体上进行有针对性的ABTest

运营阶段

  • 应用上线后持续关注线上稳定性波动,采用日报等方式进行监控,当崩溃率在超出阈值、或趋势发生波动时,及时报警通知相关方
  • 当发生无法规避的异常时,采取回滚和降级策略(参考下文的“业务高可用性方案建设”一节)
  • 版本前期关注增量异常,在处理完增量异常后,定期对存量异常进行整治,以期降低大盘崩溃率

业务高可用性方案建设

稳定性优化不仅仅要降低崩溃率,其根本目标是保证业务的高可用性。有时代码里发生异常后,虽然应用没有崩溃,但业务上处于一个不可用的状态,比如页面跳转失败、接口请求失败后无重试,等等。这些场景也是我们竭力要避免的。

要提高业务的可用性,有以下思路:

  1. 梳理业务流程,对于关键和核心路径进行埋点统计,尤其是页面加载成功率、下载安装成功率等
  2. 通过AOP等方式进行无侵入数据采集,既可以全面覆盖防止遗漏,还能降低开发成本
  3. 建立数据大盘,采用IM消息、每日邮件等方式,推送给利益相关者。数据可以分为业务数据和技术数据两类
  4. 建立报警策略,通常有以下几种
    • 阈值报警:某项指标的绝对值,如登录成功率、支付成功率、未导致崩溃的异常发生率
    • 趋势报警:相比同期的变化
    • 特定指标报警:单次发生即上报,如支付失败
  5. 针对特定用户的问题,如果在测试、开发环境难以复现,可以对其采取全量日志回捞的方式进行信息采集
  6. 发现异常后,采用兜底策略降低损失
    • 在配置中心通过设置开关,关闭相应功能入口
    • 如果是服务器下发的跳转参数,可以在服务器下发数据时,修改为跳转到正常页面
    • 采用热修复等方式替换掉异常逻辑

客户端容灾方案建设

针对异常导致的崩溃,如果我们在收到线上用户反馈后,采用本地调试、开发、测试、上线、灰度、全量的处理流程,周期是比较长的,在这期间问题的影响可能会进一步增大,而问题影响人数是线上事故定级的重要参考指标,因此不能完全依赖传统开发流程来做容灾方案。

面试问答

做了哪些稳定性方面的优化?

主要是三方面的优化:

  • Crash长期治理:我们把Crash分为增量和存量两部分,对于开发版本引入的增量Crash采取重要且紧急的节奏处理;对于历史版本就存在的存量Crash,则定期进行专项整治
  • 性能优化:主要是内存方面,因为业务场景里有比较多的图片信息流展示,对内存比较敏感,因此在开发时格外注意内存的波动变化,尤其是内存泄漏问题。对于图片,则采用一些手段来控制其内存占用,例如缓存清理、大图分区域展示、低端设备显示降级等
  • 业务稳定性优化:在关键业务路径采用AOP等无侵入的方法进行埋点,分为业务指标和技术指标,业务指标与用户使用统计情况挂钩,如用户活跃、留存、下载成功率等;技术指标则记录了接口访问成功率、访问耗时、启动耗时、Exception发生率等

通过以上优化措施,实现了应用的高可用性,在日活千万量级的前提下,线上崩溃率控制在0.2%(千分之2)以内。

具体是怎么做的?

可以展开讲性能优化里的内存优化部分

发生线上异常如何快速止损?

参考上文的《客户端容灾方案建设》

如何长期保障业务稳定性?

参考上文的《业务高可用性方案建设》

参考资料

转载自:https://juejin.cn/post/7366177423775891456
评论
请登录