Skip to content

28崩溃报告:如何借助崩溃报告解决线上的Bug?

App 在运行过程中发生闪退会给用户带来极其恶劣的体验,因此,用户往往会把经常闪退的 App 直接删掉。同样地,对开发者来说,重现线上问题也是件困难的事,因为这些 Bug 可能与用户使用时的网络连接状态、iOS 系统版本、内存空间、是否越狱等有关。那有没有什么好办法能帮助我们解决线上的 Bug,并提升用户体验呢?

崩溃报告是一种解决线上闪退问题的有效办法。崩溃报告可以实时收集真实用户在使用 App 过程中发生闪退的信息,并将其解释成对开发者友好的报告,这可以很好地帮助我们确认和诊断线上的问题。

可以这么说,崩溃报告服务已经成为 App 不可或缺的支撑功能。在 Moments App 中,我选择了 Firebase Crashlytics 作为崩溃报告服务。与市面上其他服务相比,Firebase Crashlytics 有以下 5 个优点。

  1. Crashlytics 产品有 10 年的历史,经过这快 10 年的实践检验,我们发现该产品非常稳定。

  2. Crashlytics 能同时支持 iOS 和 Android 等平台,方便我们在同一个地方查看所有 App 的崩溃报告。

  3. Crashlytics 完美地整合在 Firebase 里面,可以与 Firebase 其他服务一同使用,例如可以配合性能监控一起使用。

  4. 与 Firebase 的其他产品一样,Crashlytics 可以免费使用。

  5. fastlane 支持 Crashlytics 的整合,只需要简单的配置就可以通过 CI 自动化上传 dSYM 文件。

下面我们就来看看如何在 Moments App 里面使用 Crashlytics。

配置 Crashlytics

在使用 Crashlytics 前,我们需要完成一次性的配置。

首先登录到 Firebase 网站,并通过位于左边 Crashlytics 菜单打开 Crashlytics 页面,接着点击"Enable Crashlytics"按钮来启动 Crashlytics 功能。

启动 Crashlytics 服务以后,在 Podfile 文件里添加以下的 Pod:

java
def thirdparty_pods
  pod 'Firebase/Crashlytics', '= 7.0.0'
  pod 'Firebase/Performance', '= 7.0.0'
end

其中,Firebase/Crashlytics是用于崩溃报告服务的 Pod,而Firebase/Performance是用于性能监控的 Pod。完成上面的配置以后,只需重新执行bundle exec pod install命令就能完成 Crashlytics 的安装了。

最后一步是调用FirebaseApp.configure()函数来启动崩溃报告服务。如果你已经使用了统计分析服务,那么这一步之前就做过了。

自动化上传 dSYM 文件

完成了上述的配置以后,一旦发生闪退,在 Firebase Crashlytics 页面就能看到相关的闪退信息,除此之外,你还可能会看到以下的警告页面:

该页面告诉我们"Missing required dSYMs",中文意思就是"缺了必需的 dSYM 文件"。那什么是 dSYM 文件呢?

当 Xcode 在把源代码编译成机器码的时候,编译器会生成一堆 Symbol(符号)来存放类型的名字、全局变量和方法的名称等,这些 Symbol 会把机器码对应到各种类型所在的文件和行号。因此,我们可以利用这些 Symbol 在 Xcode 里面进行 Debug,或者在崩溃报告上定位 Bug。默认情况下,当我们生成一个 Debug 版本的 App 时,所有的 Debug Symbol 都会自动存放在 App 里面。

但是 Release 版本的 App 却不一样,为了减小 App 的尺寸,编译器并不把 Debug Symbol 存放在 App 里面,而是生成一些额外的 dSYM 文件(Debug Symbol file)来存放。每个可执行文件、Framework 以及 Extension 都通过唯一的 UUID 来配对相应的 dSYM 文件。为了便于定位线上 App 的问题,我们需要保存这些 dSYM 文件,并上传到崩溃报告服务上去。

幸运的是,fastlane 提供了一个upload_symbols_to_crashlyticsAction 来帮我们简化上传 dSYM 文件的操作。上传 Internal App dSYM 文件的具体实现如下:

ruby
desc 'Upload symbols to Crashlytics for Internal app'
lane :upload_symbols_to_crashlytics_internal do
  upload_symbols_to_crashlytics(
    dsym_path: "./Moments.app.dSYM.zip",
    gsp_path: "./Moments/Moments/Configurations/Firebase/GoogleService-Info-Internal.plist",
    api_token: ENV["FIREBASE_API_TOKEN"]
  )
end

在调用upload_symbols_to_crashlyticsAction 时,我们需要传递三个参数:首先把 dSYM 文件的路径传递给dsym_path参数,然后把 Firebase 的配置文件传递给gsp_path参数,最后是把 Firebase API Token 传递给api_token参数。在前面的《25 | 自动化构建:解决大量重复性人力工作神器》里我们已经讲述过如何获取这个 Token 了,我们是将FIRBASE_API_TOKEN环境变量配置在 local.keys 文件里面。

接下来我们再一起看看上传 AppStore 版本 dSYM 文件的具体实现:

ruby
desc 'Upload symbols to Crashlytics for Production app'
lane :upload_symbols_to_crashlytics_appstore do
  upload_symbols_to_crashlytics(
    dsym_path: "./Moments.app.dSYM.zip",
    gsp_path: "./Moments/Moments/Configurations/Firebase/GoogleService-Info-AppStore.plist",
    api_token: ENV["FIREBASE_API_TOKEN"]
  )
end

可以看到,upload_symbols_to_crashlytics_appstoreupload_symbols_to_crashlytics_internal的实现基本一样,唯一不同的地方是upload_symbols_to_crashlytics_appstore把 GoogleService-Info-AppStore.plist 文件传递给了gsp_path参数。

有了这些 Lane 以后,我们就可以修改 CI 的配置来自动完成上传 dSYM 文件的操作。下面是 .travis.yml 的配置:

yaml
- stage: "Archive, sign and deploy internal app"
  name: "Archive Internal app"
  if: branch = main
  script:
    - bundle exec fastlane archive_internal
    - bundle exec fastlane upload_symbols_to_crashlytics_internal # 新增的步骤
    - bundle exec fastlane deploy_internal

可以看到,我们在script下增加了upload_symbols_to_crashlytics_internal步骤。

查看崩溃报告

得到了上传的 dSYM 文件以后,Crashlytics 就能自动处理 dSYM 文件,并把崩溃信息解释成对开发者友好的报告,如下图所示:

报告中最关键的信息是堆栈回溯(Trace Stack),它会把 App 闪退前所调用的方法名称、代码执行的行号都按顺序依次打印出来,这样能方便我们对照着源码来定位问题。

另外,Crashlytics 还能把收集到的设备信息显示出来,方便我们重现和诊断问题,这些信息如下图所示:

如果我们同时使用了 Firebase 的统计分析服务,那么 Crashlytics 还会给我们提供闪退前的用户行为事件,方便我们按照这些步骤来重现问题,如下图所示:

除了提供崩溃报告以外,Crashlytics 还能提供可配置的警告信息,Crashlytics 会根据崩溃率的阈值来给我们及时发送警告通知。例如,下面的配置表示当有 0.1% 的用户在最近一小时内发生闪退时就发送警告通知。

性能报告

因为我们安装了Firebase/PerformancePod,所以 Firebase 会自动生成性能监控报告,如下图所示:

这些报告能为我们提供网络运行状态、屏幕呈现速度、App 启动速度等指标。

这里我们还可以为各个指标配置不同阈值与目标值来进一步监控 App 的性能状况。比如,下图显示了 App 启动速度指标的详细信息。

除了启动速度以外,Crashlytics 还提供了各种脱敏信息,例如操作系统的版本、设备的类型等,这些信息能帮助我们更准确地定位性能问题的瓶颈。

总结

在这一讲中,我们讲述了如何使用 Firebase Crashlytics 来收集崩溃报告,还讲解了如何使用 fastlane 来开发上传 dSYM 文件的操作,以及通过 CI 的配置来完成全自动化上传。通过与统计分析服务相结合,Crashlytics 能提供详细的崩溃信息,帮助我们快速地诊断和定位线上的 Bug,从而降低崩溃率,提升用户的使用体验。

这里我再分享一些减少闪退的有效办法。你可以根据项目的具体情况,合理配置崩溃率的阈值 ,并随着 App 质量的提高而不断降低崩溃率的阈值配置。然后,在发布新版本的时候采用分阶段发布的方式,例如,通过发布 1% 的用户来观察崩溃率是否提升,一旦超过一定的阈值就马上暂停发布,修复好引起崩溃的 Bug 后再重新发布新版本。总之,结合我自己的开发经验来看,通过合理配置崩溃率阈值和分阶段发布的方式,可以在很大程度上降低闪退的概率,所以,在你的开发工作中,建议你可以考虑使用。

思考题

请问你是通过什么办法来解决线上崩溃的问题呢?能分享一下你的经验吗?

请把你的答案写到留言区哦。下一讲我将介绍"如何使用远程开关来远程遥控上线 App 的产品行为"的相关内容,记得按时来听课。

源码地址

Fastfile 文件地址:https://github.com/lagoueduCol/iOS-linyongjian/blob/main/fastlane/Fastfile#L264-L281

.travis.yml 文件地址:https://github.com/lagoueduCol/iOS-linyongjian/blob/main/.travis.yml#L35