在之前的一篇博文中,我们聊了监测App的性能的重要性。这次我们就来给你示范具体怎么操作。我们向全球最受欢迎的一些 App 开发团队(包括微信,雅虎新闻摘要)请教了关于开发流畅 App 的最佳实践经验。通过我们自己的经验以及与这些顶尖开发者的交流,我们发现开发优秀 App 最重要的实践经验是建立一个性能优化的流程:
优化流程
当你的 App 性能不佳时,持续测量 App 的性能表现就变得尤为重要。一旦检测出性能问题,你应专注于对问题寻根溯源。诊断可能还会用到更多的测量和详细的性能数据,使你暂时陷入我们称之为“测量-分析循环”的怪圈。即使在找到问题的根本原因后,光修复烂代码还不够。你还得重新验证各项指标来确定修复是有效的。这就意味着更多的测量。
测量
有两个最为影响用户体验 (UX) 的性能指标。第一,我们要注意的是响应时间:你的 App 要花多久时间来响应用户的操作 (比如启动 App, 浏览新闻, 加载联系人列表, 或者打开 Facebook 页面)。理想状况下你的 App 在所有上述情景中都能迅速响应,这将帮助你建立一个更好更完美的用户体验。
一个关键(而独特)的响应时间的例子是启动时间。App 的启动是装了这个 App 的用户的第一次用户体验, 而第一印象又是最重要的。事实是,Compuware公司的研究发现79%的用户在装了有启动问题的 App 后只重试了一两次就卸载了。
以下是 NimbleDroid 团队 给出的一些建议,他们在软件性能及优化领域有着多年经验。
我们建议不要让你的 App 花在启动上的时间超过两秒,因为这是用户期待中 App 启动时间的中位数。熟悉网页性能的都知道,47%的用户希望页面能在两秒以内加载完毕,而用户们在外赶时间时花在移动端 App 上的耐心就更少了。
建议一:将启动时间限制在两秒之内
第二个重要指标是流畅度。即使一个 App 总体上来看响应时间很短,响应本身必须也要流畅,使“卡顿”尽可能少。用户对时间上的卡顿非常敏感,意味着即使是小小的顿挫也会影响用户体验。平均来说,人眼能分辨小到 22 毫秒的卡顿,而四分之一的人群能察觉 2 毫秒到 16 毫秒的卡顿 – 60帧每秒 (FPS) 的刷新率就是根据这个原理。
你可以从你的 App 的 FPS 和帧时间数据得出 App 的流畅度。然而请牢记这数据不会告诉你你的 App 的性能问题根源所在,这就意味着它没法帮你找到 App 卡顿的 method。
在安卓系统中,用户界面 (UI) 线程 (你的 App 执行的主线程) 是唯一能对用户界面进行更新的线程。要保持 60 FPS 的刷新率,UI线程必须在每 16 毫秒内完成一帧的绘制。如果在 UI 线程上的任何方法调用时间超出了这一时间, 你的 App 就会掉帧, 产生时间上的卡顿。更糟的是此时你的 App 对任何的用户操作都无响应,因为 UI 线程还在被这个 method 调用所占据。
实际上,要确保 UI 线程的每一个 method 的调用时间都少于16毫秒是几乎不可能的。32 毫秒的阈值,相当于两个帧的长度,才更合理。我们把超出这个门槛值 (执行时间超出32毫秒) 的方法叫做挂起方法,因为它们使得 App 看上去“挂起”了。为了使你的 App 丝般顺滑,收获更佳的用户体验, 消除所有的挂起方法 将大有裨益。
建议二:消除挂起 methods
好吧。既然测量同用户体验相关的指标是如此重要,那么我们该多久测量一次指标呢?每个版本?每日构建版本?在每次发布之前?在生产中?!你应当一有机会就进行测量 —— 测量的频率越高,你就能越早发现和解决性能问题。同我们交流的 Yahoo 团队在每次发布前对 App 的性能进行采样,而微信团队对每个日版本进行性能采样。
建议三:尽可能多测量
分析
优化你的软件的关键是找到常见的性能问题并系统地从你的代码中剔除。在对有着超过五百万次下载记录的 App 的性能问题分析中,我们观察到开发者经常使用在桌面端高效却在移动端低效的代码结构。举个例子,在一台 MacBook Air 上一个 JAR 文件调用methodClassLoader.getResourceAsStream() 处理3K 资源的花销约为 7 毫秒, 而在一台 2013 年的 Nexus 7 上一个 APK 文件调用这个 method 同样处理3K 资源的花销约为 1700 毫秒。后来证明是安卓对于getResourceAsStream 的执行是在第一次调用时做了大量的额外工作,如为 APK 文件里的所有资源编排索引,验证 APK 文件的证书, 并解析其 manifest。这种操作是相当耗费 CPU 资源的,从而导致 App 的严重时滞 ——getResourceAsStream 给 Walgreens App 造成了 1.7 秒的卡顿。在NimbleDroid,我们列出了一份应该避免在你的安卓 App 中使用的 methods 清单。请戳这里。
建议四:了解一些通病
有时性能问题是由你使用的第三方 SDK 引起的,而并非你的代码。这些问题就很难被发现。譬如说org.joda.time ,Java中一个流行的时间库。你可能在以往的Java项目上使用过它。结果仅仅是创建一个 org.joda.time.DateTime() 对象就造成了巨大的时滞 —— Yahoo Fantasy Sports App 性能被它拖慢了两秒之多。这是因为org.joda.time.DateTime() 使用getResourceAsStream() 来从 APK 文件加载时区数据。
建议五:避免第三方 SDK 带来的麻烦
优化
修复烂代码可能成为噩梦般的过程。要从上百个进程中找出拖慢 App 的进程,这种寻根溯源的任务可能会占据数周的开发周期。尽管如此,还是有一些通用的指导方针的。使用更有效的数据表现方式,算法,和执行方式或(万一你使用的是SDK从而无法直接修复代码的话)在后台线程调用代码而避免挂起在 UI 线程发生,这里面任意一种方式,都能使你的代码运行得更快。遵循这些指导方针使 App 更有效率,大大有益于创造出人见人爱的产品。
去哪寻求帮助
尽管性能优化过程帮助你的 App 运行顺畅,它也需要花费时间和一点点魔力来实施。这正是为什么我们要开发安卓快速开发,强力优化以及profiling tools工具的原因,这样你们就能不分心地专注于你们的强项:为用户带来了不起的产品。