用户说“App 卡死了”,你却查不到原因?可能是监控方式错了


作者:高玉龙(元泊)

背景介绍

当用户使用 App 时产生不好的体验,问题往往出现在以下场景:

  • 打开复杂页面时出现黑屏/白屏延迟
  • 列表滑动时偶发性卡顿
  • 图片加载时界面响应滞后
  • 网络请求密集时出现操作卡死等现象

这些场景不仅出现在低端设备上,在中高端机型中同样存在。如果主线程无法响应用户的交互就会造成卡顿,卡顿时间比较长是比较影响 App 的功能和用户体验的。在移动应用开发中,卡顿问题也始终是影响用户体验的核心痛点。

通常情况下,导致主线程阻塞并引发卡顿的原因主要有以下几种:

  • 繁重的 UI 渲染:当界面包含复杂的视图层级、大量的图文混排内容时,计算布局和绘制到屏幕上的工作量会急剧增加,超出单次刷新周期的处理能力。
  • 主线程同步网络请求:在主线程中发起同步的网络调用,意味着整个应用必须等待网络数据返回后才能继续执行,期间无法响应任何用户操作。
  • 大量的文件读写(I/O):在主线程上直接进行大规模的数据读取或写入操作,例如读写数据库或本地文件,会因为磁盘速度的限制而消耗大量时间。
  • 高负荷的计算任务:将复杂的算法或大量数据的处理逻辑直接放在主线程执行,会导致 CPU 持续处于高占用状态,无暇顾及 UI 事件。
  • 线程锁使用不当:当主线程需要等待其他线程释放某个锁资源时,它会被挂起,如果等待时间过长,便会造成卡顿。在极端情况下,不同线程间的相互等待还会引发”死锁”,导致应用彻底无响应。

由于这些问题的偶发性和环境依赖性,传统的线下调试手段往往难以奏效。为了能够精确、高效地定位并解决这些线上卡顿,我们进行一些卡顿监控技术的探索。

主流卡顿监控方案

在 iOS 开发中,以下是几种常见的主流卡顿监控方案:

  • Ping 线程方案
  • FPS 监控方案
  • RunLoop 监控方案

Ping 线程方案简介

Ping 线程方案的核心思想是:

  • 创建一个子线程,通过子线程来”探测”主线程的响应能力。
  • 子线程每次 ping 主线程时设置标记位为 YES,然后派发任务到主线程中,主线程把标记位设置为 NO。
  • 子线程 sleep 指定时间,超时后判断标记位是否设置为 NO,如果没有说明主线程发生了卡顿。

如下图所示:

关键实现步骤:

  1. 创建子线程:启动一个独立的监控线程。

  2. 定时派发任务:子线程定期向主线程派发一个简单的任务,并设置一个等待标记。

  3. 等待主线程响应:主线程执行该任务时,会回调子线程,并清除等待标记。

  4. 超时判断:如果子线程在派发任务后的一小段时间内,发现等待标记仍未被清除,则判定主线程卡顿。

  5. 捕获与上报:执行堆栈捕获和上报流程。

Ping 线程的方案逻辑相对比较简单,也比较容易理解。但精度较差,Ping 之间可能存在漏查的情况。同时,Ping 线程会不停唤醒主线程 RunLoop,也会存在一定的性能损耗。

FPS 监控方案简介

通常情况下屏幕会保持 60Hz/s 的刷新速度(新的 iOS 设备甚至会保持 120Hz/s 的刷新速度),每次刷新时会发出一个屏幕刷新信号,CADisplayLink 允许开发者注册一个与刷新信号同步的回调处理。

我们可以通过计算它 1 秒内调用多少次来查看界面的流畅度。虽然 CADisplayLink 更轻量,但需要在 CPU 稍微清闲时才能够回调,严重卡顿的堆栈获取不一定及时,并且就算 50fps 以下通过肉眼来看也是连贯的。所以,简单的通过监控 FPS 很难确定是否出现了卡顿问题。

RunLoop 方案简介

基于 RunLoop 的监控方案,是目前比较主流的可用于生产环境的监控方案。其原理是利用 CFRunLoopObserver 来观察主线程 RunLoop 的状态变化。这里通过引用戴铭关于 RunLoop 原理的图,来对 RunLoop 方案的原理进行简单介绍:

  • 通知 observers:RunLoop 即将开始”进入 loop”
  • 随后,会开启一个 do while 来保活线程
    • 通知 obersers:RunLoop 会触发 Timer、Source0 回调,紧接着执行加入的 block
    • 如果 Source1 是 ready 状态,会跳转到”处理消息”流程
  • 通知 observers:RunLoop 即将进入休眠状态
  • 等待 mach_port 消息,以再次唤醒
    • 基于 port 的 Source 事件
    • Timer 时间到
    • RunLoop 超时
    • 被调用者唤醒
  • 通知 observers:RunLoop 被唤醒
  • 处理消息
  • 继续下一个 loop
未经允许不得转载:紫竹林-程序员中文网 » 用户说“App 卡死了”,你却查不到原因?可能是监控方式错了

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
关于我们 免责申明 意见反馈 隐私政策
程序员中文网:公益在线网站,帮助学习者快速成长!
关注微信 技术交流
推荐文章
每天精选资源文章推送
推荐文章
随时随地碎片化学习
推荐文章
发现有趣的