大多数浏览器和
Developer App 均支持流媒体播放。
-
使用 XCTest 消除动画障碍
动画效果能够显著提升 app 的用户体验、为用户提供直接操控的感触、以及帮助用户理解其操作在 app 中造成的效果。但如果动画效果出现卡顿,则可能破坏用户体验。了解如何运用 XCTest 侦测平滑滚动及动画效果的终端,并学习如何在 app 用户受到影响前阻止问题发生。
资源
相关视频
WWDC21
WWDC20
-
下载
(你好 WWDC 2020)
大家好 欢迎来到 WWDC (使用 XCTEST 消除动画障碍) 大家好 我叫 Tanuja Mohan 是 Apple 公司 动力和性能团队的软件工程师 动画是我们 app 客户体验中的重要部分 动画巧妙多变 比如当我们点击后退按钮 从一个屏幕切换到另一个屏幕时 又可以成为手势的主要焦点 比如当我们在 app 上向上或向下滚动时 我们希望这些手势能产生流畅的响应 因为在长时间浏览期间或滚动出现抖动时 就会产生明显感觉
我们将这些用户可感知的抖动称为“障碍” 当帧在屏幕上的出现时间迟于预期时 就会出现障碍 从而分散了用户的注意力 并且有损于 app 的感知质量 让我们放大 app 的各个帧 看看到底是怎么回事 如预期那样显示前三帧 就会出现列表的渐变移动 完美匹配我们手指的移动
但是第三帧仍然留在屏幕上 该 app 似乎不再跟随你手指的移动
然后 当第四帧显示到屏幕上时 列表突然大幅度跳回到你的手指上 这不是我们所期望的 我们希望避免这种情况出现 要理解这里发生了什么 首先需要理解帧是如何显示到屏幕上的 iPhone 和 iPad 上的帧 通常以 60 赫兹更新 每帧的节奏为 16.67 毫秒 在 iPad Pro 上 可以达到 120 赫兹的更新 或者是 8.33 毫秒的步频 这种步频由 VSYNCs 表示 它是屏幕决定是否 将帧交换到显示器上的时间 当帧错过预期的 VSYNC 时 就会出现障碍 障碍的严重程度 是通过帧在屏幕的显示延迟来衡量的
在本例中 第四帧晚了 16.67 毫秒
有两种方法可以量化障碍 (障碍指标) 障碍时间是帧在屏幕的延迟显示时间 以毫秒为单位 我们更倾向于表示为障碍比率 即毫秒每秒 它是以毫秒为单位的总障碍时间 在以秒为单位的其他持续时间中的比例 例如 在测试的持续期间 听起来很复杂 我们为什么不直接说掉帧 和每秒测量帧数呢? 每秒帧数是一个极易偏斜的绝对目标 如果你的测试在动画执行期间 包含任何休息时间 那么每秒帧数则是毫无作用的 因为我们不会在休息期间 交换任何帧
而且我们经常故意 不以最大每秒帧数为目标 也许只想以 30 帧每秒的速度运行游戏 或者只想以 24 帧每秒的速度运行视频 出于功耗和性能的考虑 时钟 app 图标上的指针 仅以 10 每秒帧数的速度运行
障碍时间的目标始终为零 即使考虑到这些因素也是可靠的 然而 障碍时间并不总是具有可比性的 一秒测试的总故障时间 不能与十秒测试的总故障时间相比较
通过将障碍比率归一化为 每秒测试持续时间中的障碍毫秒数 就能得到一个 在不同测试中都具有可比性的度量 并可用作最终用户影响的近似值 对于最终用户的影响 以下是我们建议的目标障碍比率 并可用于工具之中
每秒不到五毫秒的障碍比率 是很好的用户体验 在每秒五到十毫秒的范围内 用户会开始注意到障碍 此时应对这些故障进行调查
在每秒十毫秒或更多的情况下 障碍会使用户分心 我们应该立即采取行动来解决它们 在 iOS14 中 你可以使用我们的工具套件 来跟踪你的开发 和生产工作流中的障碍 XCTest 框架允许你 在单元测试和用户界面测试中 直接收集障碍和动画数据 而 MetricKit 和 Xcode Organizer 则允许你访问 来自客户的性能指标 在这一部分的讨论中 我们将重点讨论 使用性能 XCTests 捕捉障碍的开发工作流 如果你想了解 如何查看生产工作流中的障碍 请查看我们为 MetricKit 和 Xcode Organizer 提供的单独讲座 (METRICKIT 中的新增功能) (诊断 XCODE ORGANIZER 的性能问题) 在 Xcode11 中 我们介绍了 XCTMetrics 这些指标指定了你想要在测试中 测量系统的部分 我们现在可用的 XCTMetrics 允许你测试时钟时间 中央处理器利用率、内存使用、 os_signposts、存储容量 而在 XCode 12 中 我们有单独的指标来测量 app 启动时间 我们也有模板供你编写自己的自定义指标
在本次讨论中 我们将重点讨论 XCTOSSignpostMetric 它是用于做动画性能测试的 XCTMetric
从 Xcode11 开始 你可以使用 XCTOSSignpostMetric 来测量 os_signpost 间隔的持续时间 而在 Xcode12 中 使用动画 os_signpost 间隔 不仅能收到持续时间 而且还收到三个与障碍相关的指标 帧速率和帧数
你可能已经熟悉帧速率和帧数 这两个值分别测量 显示到屏幕的帧数和频率
现在你也很熟悉障碍了 你可以跟踪测试代码块中发生的障碍次数 在测试中发生障碍的总持续时间 以及该总障碍时间 与测量代码块持续时间的比率
要收集这些指标 首先需要检测代码 以发出 os_signpost 间隔 有三种方法可以做到这一点 我们将它们称为非动画间隔 仅返回持续时间的间隔 以及动画间隔 返回附加动画指标的间隔 在 Xcode11 中 你只能使用“begin”和“end”接口 检测非动画 os_signpost 间隔 只能返回持续时间
那么在 Xcode12 中 要指定动画 os_signpost 间隔 你只需使用 animationBegin 接口 只需做此更改 你就可以转换任何现有的检测 以发出动画间隔 并接收回前面提到的动画指标
除了使用自定义时间间隔外 也可以使用 预定义的 UIKit 检测间隔之一 来测试浏览转换和滚动 这些是 XCTOSSignpostMetric 类上 提供的子指标 让我们来看看 使用其中一个子指标编写测试的示例 这里有一个性能 XCTest 它将启动我的 app 点击“Meal Planner”单元 然后向上轻扫 foodCollection 视图 接着向下滚动
在此测试中 我指定要测量滚动减速度子指标
在测量块的主体中 我向上轻扫 现在有了 Xcode12 你就可以自定义滚动的速度
这个测试到目前为止看起来还不错 但是我们可以改进一下 请记住 在默认情况下 指标块将运行五次以收集性能度量值 这意味着在当前实现中 我们要背靠背地向上轻扫五次 并且很可能在每次迭代中 滑动不同的内容
为了避免这种情况 我们希望在运行之间重置 app 状态 我们可以通过使用 XCTMeasureOptions 让指标块知道 我们将手动停止指标集合 然后 将其传递到指标块 调用 stopMeasuring 然后重置 app 状态 编写好测试之后 就可以开始运行 但在此之前 我们首先要 修改测试方案中的一些设置 以消除它们对性能测量的影响 首先 我们要确保使用 单独的性能测试方案 XCTESS
然后选择发布构建配置并禁用调试器
还需禁用自动截图收集 关闭代码覆盖率
最后要关闭所有诊断选项 这些是运行时净化下列出的选项 运行时 API 检查和内存管理 现在我们可以运行性能 XCTest 并在用户界面报告中查看结果 在下拉列表中 我们可以看到新的动画指标 让我们选择 Hitch Time Ratio 指标
然后就能看到收集了五个迭代的测量值
并得到了平均 1.2 毫秒/秒的障碍时间比
下一步 我们可以将 1.2 ms/s 的平均值 设置为基线 以便将来该测试的任何运行 都将与该基线值进行比较
让我们看一个例子 看看在代码库中可能会遇到的障碍 以及该如何使用性能 XCTest 来防止它发送给你的客户
假设我是一家餐饮策划公司的 app 开发人员 该公司希望支持在线订餐和送货 到目前为止 我已经实现了可用的 不同菜单项的视图
下一步 我想通过加入不同菜肴的图像 来让我的食物更有吸引力 在我开始编写这个新特性之前 我想衡量一下现在的动画表现 所以我可以用它作为基线 以便在添加特性之后进行比较
我已经为我的性能测试设置了 一个单独测试方案 并将设置配置为我们前面谈到的内容 现在开始编写测试了
正如我们之前看到的 我要启动 app 点击 Meal Planner 单元 并测量滚动动画性能
我已经预先运行了这个测试 现在来看看测量结果
看来没有任何问题 我们的动画表现也和预期的一样 让我们继续添加新特性
首先 我要将图像视图设置为包含 我已经预先包含在项目中的图像
其次 我将缩放这些图像 使它们与我的 app 尺寸匹配
现在我们可以继续并重新运行性能测试
请注意 当使用 XCTOSSignpostMetric 时 测量块将侦听检测的 os_signpost 间隔 何时在代码块中发出 并仅收集在此间隔内执行的代码指标 另外需要注意的是 测量块支持侦听 多个不同的 os_signpost 间隔 例如 你可以在调用 swipe up 的 同一代码块中 同时监听滚动减速 和滚动拖动 os_signpost 间隔 让我们提前完成这个测试
看来障碍增多了 我们应该立即进行调查
看起来问题 就在 scaleAspectFit 调用中 我们正在主线程上重绘图像 主线程负责渲染用户界面的其余部分 我们使用的是 CPU 它能创建新的像素和分配内存
我们可以通过使用核心动画的 setContentMode 来减少这种影响 它将把重绘这些图像交给图形处理器 可允许我们使用现有的图像像素 以减少在主线程上所做的工作量
我们可以再次重新运行性能 XCTest 看看是否解决了问题
我们可以确认动画指标现在报告为零障碍 性能又回到了我们的期望值
使用性能 XCTest 让我们看到新特性可引起回归 给了我们机会去进行修复 现在我们的特性 已经准备好与我们的客户见面了
让我们复述一下所讨论过的内容 我们了解到 当帧在屏幕上的出现时间迟于预期时 就会发生障碍 我们可以使用所推荐的 “良好”“警告”和“严重”类别 来量化这些障碍
然后我们了解到可以使用性能 XCTest 来捕捉开发工作流中的障碍 通过使用 UIKit 或自定义动画 os_signpost 间隔就可实现这一点 我们还谈到了最佳做法 其中包括在迭代之间重置 app 内容 以及配置方案设置以防止误差 有了这些知识 你现在就可以防止代码库中的障碍 并为客户提供流畅的动画体验 感谢你的聆听 我们希望你参会愉快
-
-
6:35 - Create an animation os_signpost interval
os_signpost(.animationBegin, log: logHandle, name: "performAnimationInterval") os_signpost(.end, log: logHandle, name: "performAnimationInterval")
-
6:55 - Use a UIKit instrumented animation os_signpost interval
extension XCTOSSignpostMetric { open class var navigationTransitionMetric: XCTMetric { get } open class var customNavigationTransitionMetric: XCTMetric { get } open class var scrollDecelerationMetric: XCTMetric { get } open class var scrollDraggingMetric: XCTMetric { get } }
-
7:12 - Measure scrolling animation performance using a Performance XCTest
// Measure scrolling animation performance using a Performance XCTest func testScrollingAnimationPerformance() throws { app.launch() app.staticTexts["Meal Planner"].tap() let foodCollection = app.collectionViews.firstMatch measure(metrics: [XCTOSSignpostMetric.scrollDecelerationMetric]) { foodCollection.swipeUp(velocity: .fast) } }
-
8:02 - Reset the application state between runs
func testScrollingAnimationPerformance() throws { app.launch() app.staticTexts["Meal Planner"].tap() let foodCollection = app.collectionViews.firstMatch let measureOptions = XCTMeasureOptions() measureOptions.invocationOptions = [.manuallyStop] measure(metrics: [XCTOSSignpostMetric.scrollDecelerationMetric], options: measureOptions) { foodCollection.swipeUp(velocity: .fast) stopMeasuring() foodCollection.swipeDown(velocity: .fast) } }
-
-
正在查找特定内容?在上方输入一个主题,就能直接跳转到相应的精彩内容。
提交你查询的内容时出现错误。请检查互联网连接,然后再试一次。