最近在学习张绍文大佬的《Android开发高手课》,整理成笔记以作备忘,想深入了解的推荐去极客时间购买大佬的原版课程
开篇-焦虑的移动开发者如何破局
移动互联网的发展不知不觉已经十多年了,Mobile First 也已经变成了 AI First。换句话说,我们已经不再是“风口上的猪”。可以说,国内移动互联网的红利期已经过去了,现在是增量下降、存量厮杀,从争夺用户到争夺时长。移动端的招聘量变少,但中高端的职位却多了起来,这说明行业只是变得成熟规范起来了。竞争激烈,但产品质量与留存变得更加重要,我们进入了技术赋能业务的时代。不要把时间浪费在纠结问题上,而是应该放在解决问题上。
- 一个应用至少会经过开发、编译 CI、测试、灰度和发布这几个阶段;
-
Android 绿色联盟开发者大会上推出的应用体验标准,有对应用的兼容性、稳定性、性能、功能和安全做了详细的定义;
- 我们很多时候都在用战术的勤奋掩盖战略的懒惰,性能优化的关键在于如何解决存量问题,同时快速发现增量问题;
下面开始高质量开发篇
崩溃优化(上)
崩溃率是衡量一个应用质量高低的基本指标;
-
Android 崩溃分为 Java 崩溃和 Native 崩溃:
-
Java 崩溃就是在 Java 代码中,出现了未捕获异常,导致程序异常退出;
-
Native 崩溃又是怎么产生的呢?一般都是因为在 Native 代码中访问非法地址, 也可能是地址对齐出现了问题,或者发生了程序主动 abort,这些都会产生相应的 signal 信号,导致程序异常退出。
-
Native崩溃的捕获流程
-
可以参考:
-
完整的 Native 崩溃从捕获到解析的流程:
-
编译端。编译 C/C++ 代码时,需要将带符号信息的文件保留下来
-
客户端。捕获到崩溃时候,将收集到尽可能多的有用信息写入日志文件,然后选择合适的时机上传到服务器。
-
服务端。读取客户端上报的日志文件,寻找适合的符号文件,生成可读的 C/C++ 调用栈。
- Native 崩溃捕获的难点
上面的三个流程中,最核心的是怎么样保证客户端在各种极端情况下依然可以生成崩溃日志。因为在崩溃时,程序会处于一个不安全的状态,如果处理不当,非常容易发生二次崩溃。那么,生成崩溃日志时会有哪些比较棘手的情况呢?
情况一:文件句柄泄漏,导致创建日志文件失败,怎么办?应对方式:我们需要提前申请文件句柄 fd 预留,防止出现这种情况。情况二:因为栈溢出了,导致日志生成失败,怎么办?应对方式:为了防止栈溢出导致进程没有空间创建调用栈执行处理函数,我们通常会使用常见的 signalstack。在一些特殊情况,我们可能还需要直接替换当前栈,所以这里也需要在堆中预留部分空间。情况三:整个堆的内存都耗尽了,导致日志生成失败,怎么办?应对方式:这个时候我们无法安全地分配内存,也不敢使用 stl 或者 libc 的函数,因为它们内部实现会分配堆内存。这个时候如果继续分配内存,会导致出现堆破坏或者二次崩溃的情况。Breakpad 做的比较彻底,重新封装了Linux Syscall Support,来避免直接调用 libc。情况四:堆破坏或二次崩溃导致日志生成失败,怎么办?应对方式:Breakpad 会从原进程 fork 出子进程去收集崩溃现场,此外涉及与 Java 相关的,一般也会用子进程去操作。这样即使出现二次崩溃,只是这部分的信息丢失,我们的父进程后面还可以继续获取其他的信息。在一些特殊的情况,我们还可能需要从子进程 fork 出孙进程。
- 选择合适的崩溃服务
对于很多中小型公司来说,并不建议自己去实现一套如此复杂的系统,可以选择一些第三方的服务。目前各种平台也是百花齐放,包括腾讯的Bugly、阿里的啄木鸟平台、网易云捕、Google 的 Firebase 等等
- 如何客观地衡量崩溃
要衡量一个指标,首先要统一计算口径。如果想评估崩溃造成的用户影响范围,我们会先去看 UV 崩溃率。UV 崩溃率 = 发生崩溃的 UV / 登录 UV 我们还可以去看应用 PV 崩溃率、启动崩溃率、重复崩溃率这些指标,计算方法都大同小异。
这里为什么要单独统计启动崩溃率呢?因为启动崩溃对用户带来的伤害最大,应用无法启动往往通过热修复也无法拯救。闪屏广告、运营活动,很多应用启动过程异常复杂,又涉及各种资源、配置下发,极其容易出现问题。微信读书、蘑菇街、淘宝、天猫这些“重运营”的应用都有使用一种叫作“安全模式”的技术来保障客户端的启动流程,在监控到客户端启动失败后,给用户自救的机会。
推荐阅读下面文章:
- 如何客观地衡量稳定性
崩溃率是不是就能完全等价于应用的稳定性呢?答案是肯定不行。处理了崩溃,我们还会经常遇到 ANR。怎么去发现应用中的 ANR 异常呢?
1. 使用 FileObserver 监听 /data/anr/traces.txt 的变化;很多高版本的 ROM,已经没有读取这个文件的权限了,只能思考其他路径,海外可以使用 Google Play 服务,而国内微信利用Hardcoder框架向厂商获取了更大的权限。2. 监控消息队列的运行时间;这个方案无法准确地判断是否真正出现了 ANR 异常,也无法得到完整的 ANR 日志, 在我看来,更应该放到卡顿的性能范畴;都有哪些应用退出的情形?1. 主动自杀:Process.killProcess()、exit() 等。2. 崩溃:出现了 Java 或 Native 崩溃。3. 系统重启: 系统出现异常、断电、用户主动重启等,我们可以通过比较应用开机运行时间是否比之前记录的值更小。4. 被系统杀死: 被 low memory killer 杀掉、从系统的任务管理器中划掉等。5. ANR。我们可以在应用启动的时候设定一个标志,在主动自杀或崩溃后更新标志,这样下次启动时通过检测这个标志就能确认运行期间是否发生过异常退出。对应上面的五种退出场景,我们排除掉主动自杀和崩溃(崩溃会单独的统计)这两种场景,希望可以监控到剩下三种的异常退出,理论上这个异常捕获机制是可以达到 100% 覆盖的。所以就得到了一个新的指标来衡量应用的稳定性,即异常率。UV 异常率 = 发生异常退出或崩溃的 UV / 登录 UV根据应用的前后台状态,我们可以把异常退出分为前台异常退出和后台异常退出;通过异常率我们可以比较全面的评估应用的稳定性,对于线上监控还需要完善崩溃的报警机制。
关于Hardcoder: 你信不信,这篇只有手机厂商能看懂
- 课后作业: 使用 Breakpad 来捕获一个 Native 崩溃,地址如下:github.com/AndroidAdva…