其他

“追踪圣诞老人”— 每年最新API和应用开发技术的试验田

2017-03-14 DevRel 谷歌开发者




文 | Google 开发者项目工程师 Sam Stern


“追踪圣诞老人”是 Google 的一个节日传统。 除了在每个圣诞季给全球数以百万计的用户带来欢乐之外,它还是每年最新 API 和应用开发技术的试验田。 正因如此,我们每年都会在 Github 上发布此应用的全部源代码:

github.com/google/santa-tracker-android


2016 年,Santa 团队挑战自我,不仅为该应用引入新内容,还使它比以往更精简、更高效。 在此文中,您可以阅读到我们如何让“追踪圣诞老人”变得更精简、更快速。


1. APK 的臃肿

经过多年的发展,“追踪圣诞老人”已包含适用于多种游戏和交互场景的视觉和音频资源。 2015 年,“追踪圣诞老人”APK 的大小为 66.1 MB。


Android Studio APK 分析器是一个非常有用的工具,可查明 2015 版应用如此庞大的原因。


首先,尽管 APK 大小为 66.1 MB,但我们看到下载大小为 59.5 MB!其中资源文件夹占很大一部分,而资源和本机内容库所占比例也较大。


2016 版应用除了包含 2015 版应用的全部内容外,还新增了四款全新的游戏。 最初,我们认为,新增这四款游戏后,精简此应用将是不可能完成的任务,但(注意,重点来了)2016 版应用的最终结果是:


现在,尽管新增了四款游戏和全新视觉效果,此应用的下载大小减少将近 10 MB。本部分的剩余内容将探讨我们是如何做到这一点的。


1)在 Google Play 上通过 APK 拆分实现多 APK 支持

2015 版应用增加了由 Google 的 Fun Propulsion Labs 团队开发的“Snowdown”游戏。 此游戏使用 C++ 编写,因此作为本机内容库包含在“追踪圣诞老人”中。 该团队为我们提供了针对 armv5、armv7 和 x86 架构编译的内容库。 每个版本约为 3.5 MB,三者相加,即为您在 2015 版 APK 的 lib 条目中看到的 10.5 MB。


由于每台设备都只使用以上三种架构之一,因此可以删除 2/3 的本机内容库,以节省空间 — 对此,我们的折中方法是发布多个 APK。 Android Gradle 构建系统原生支持为每种架构 (ABI) 构建一个 APK,它只需在应用的 build.gradle 文件中插入几行配置代码即可实现这一点:


启用拆分后,必须为每个拆分分配唯一的版本代码,以便它们可以在 Play 商店中共存:


在最新版本的“追踪圣诞老人”中,我们分别发布了适用于 armv5、armv7 和 x86 的版本。 实施此次变更后,每个变体的本机内容库从 10.5 MB 减少至 4 MB 左右,且未丢失任何功能。


2)优化图片

“追踪圣诞老人”APK 的大部分是图像资源。每款游戏有数百个图像,每个图像有多种适合不同屏幕密度的尺寸。这些图像几乎均为 PNG 格式,因此,过去几年,我们通过对所有文件运行 PNGCrush 来判断作业是否已完成。 2016 年,我们了解到,PNG 无损压缩得到了很大改进,而 Google 的  工具是目前最先进的压缩工具。


通过对所有 PNG 资源运行 zopflipng,我们可将大多数图像无损压缩 10%,有些图像甚至可以压缩多达 30%。其结果是,在不牺牲任何质量的情况下,使应用的大小缩减近 5 MB。例如,通过无损压缩,此圣诞老人图像由 10 KB 缩减至仅 7 KB。 不要费力去寻找不同之处,绝无不同之处的!


压缩之前 (10.2KB)

压缩之后 (7.4KB)


3)未使用的资源

在开发“追踪圣诞老人”时,工程师们不断重构此应用,增加和移除之前的逻辑和 UI 项。尽管代码审核和 Lint 检查有助于发现未使用的代码,但未使用的资源很有可能被遗漏。 而且,由于没有针对资源的 ProGuard,我们的工具链无法帮助检查资源,未使用的图像以及其他资源往往一不留神就混入应用中。


Android Studio 可以帮助查找未使用、却使 APK 变得臃肿的资源。 点击 Analyze > Run Inspection by Name > Unused Resources,Android Studio 将识别任何已知代码路径未使用的资源。 必须首先删除所有未使用的代码,因为死代码“占用”的资源无法被识别为未使用。


使用有用的 Android Studio 工具进行几轮分析后,我们得以发现数十个未使用的文件,从应用中额外删除了几 MB 的资源。



2. 内存使用情况

“追踪圣诞老人”风靡全球,其用户使用的 Android 设备多达数千种。 这些设备当中很多都是几年前的老产品,RAM 仅为 512 MB 甚至更小,因此,我们的游戏之前曾出现过 OutOfMemoryErrors 错误。


尽管上述优化减少了 PNG 所占用的磁盘空间,但在载入位图 (Bitmaps) 时,它们的内存占用情况并无改变。 由于“追踪圣诞老人”中的每个游戏均加载多个图像,内存使用量迅速达到危险水平。


在 2015 年,我们的十大崩溃事件中,有六起与内存有关。通过进行以下(和其他)优化,我们已将内存崩溃事件从十大崩溃事件中统统移出。


1)图像加载回退

在“追踪圣诞老人”中启动一个游戏时,我们通常会将第一个场景所需的所有位图加载至内存中,以便游戏可以流畅运行。 原来采用的方法如下所示:


然而,如果我们没有足够的 RAM 来将位图加载至内存,decodeResource 函数将引发 OutOfMemoryError。 为避免出现此情况,我们在发现这些错误后尝试使用更高的采样率(每次以 2 倍采样率递增)重新加载所有图像:


现在,使用此技术后,低内存设备中图形的像素将更高,而且,通过这一折中方法,我们几乎完全消除了由于加载位图所导致的内存错误。


2)透明像素

如上所述,图像在磁盘上的大小并不能很好地说明它的内存使用量。透明区域较大的图像就是一个明证。 PNG 可以将这些区域压缩至接近零的磁盘大小,但每个透明像素所需的 RAM 仍然不变。


例如,在“Dasher Dancer”游戏中,动画是通过一系列 1280x720 PNG 帧呈现的。 当动画对象从屏幕中消失时,其中许多帧的大部分区域是透明的。 我们编写了一个脚本,将所有透明区域修剪掉,并记录用于显示每个帧的“偏移量”,这样,总体显示的像素仍为 1280x720。在一项测试中,该脚本将此游戏运行时的 RAM 使用量减少了 60 MB!由于我们避免了将内存浪费在透明像素上,因此较少需要缩小图像,并且可以在低内存设备上使用更高分辨率的图像。



3. 探索的其他途径

除了上述重大优化外,我们还在应用精简和加速方面探索了其他几种途径,取得了不同程度的效果。


1)启动画面

2015 版应用转而采用 Material Design 的美学设计,通过中央的“卡片”列表启动游戏。我们发现,一半的游戏会在启动时导致卡片“波纹”效果反应迟钝,但我们当时未能找到其根源所在,因而未能修复此问题。


在开发 2016 版应用时,我们力争修复游戏启动慢的问题。经过数小时的调查研究,我们发现,只有固定为横向屏幕的游戏在启动时才会出现迟钝的问题。 而强制改变屏幕方向又带来了掉帧问题。为打造更加流畅的用户体验,我们在启动器 Activity 和游戏 Activity 之间引入启动画面。 启动画面将检测当前设备屏幕方向以及打开正在加载的游戏所需的屏幕方向,并在运行时自动旋转设备,使之与游戏设定的屏幕方向匹配。 这很快消除了游戏启动时任何明显的反应迟钝的问题,使整个应用的体验更加流畅。


2)SVG

我们最初接手精简资源的任务时,使用 SVG 图像似乎显然是一个最佳方案。 矢量图像要小得多,而且只需包含一次,即可支持多个屏幕密度。 由于“追踪圣诞老人”采用“扁平化”的设计美学,我们甚至可以将多个 PNG 转化为极小的 SVG,且质量损失较少。但是,在运行速度较慢的设备上根本无法加载这些 SVG,其加载速度仅为 PNG 的数十分之一乃至数百分之一(具体取决于路径复杂程度)。


最后,我们决定采纳将矢量图像大小限制在 200x200 dp 的建议,仅将 SVG 用于应用中的小图标,而不用于较大的图形或游戏资源。



结论



我们在开始构建 2016 版“追踪圣诞老人”时,面临一个非常棘手的问题:我们如何在增加新奇有趣的内容的同时,让此应用变得更精简,速度更快?通过不断相互激励,力求以更少的资源发挥更大的作用,并在做出每项变动时始终考虑资源限制因素,我们发现了以上优化措施。最终,我们得以使“追踪圣诞老人”应用一如既往地正常运行……接下来我们的任务将是帮助 Claus 先生逐步降低所有附加 Cookie 所占的比重。


推荐阅读:

谷歌开源游戏“Google追踪圣诞老人”更新

Android Studio 2.3正式版发布,官方全解析

让Android支持内容库保持最新

除了Material Design,Android Wear 2.0还有哪些看点?


您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存