[译] 谷歌寻踪圣诞老人应用(Santa Tracker)迁移到 Android App Bundle 记录

谷歌寻踪圣诞老人应用(Santa Tracker)迁移到 Android App Bundle 记录html

本文是 2018 谷歌寻踪圣诞老人应用改进探索系列文章的第一篇。前端

寻踪圣诞老人是谷歌每一年都会发布的一款应用,这款应用让人们能够在全球追寻圣诞老人的足迹。不幸的是,这款应用在通过几年的迭代后,体积剧增,2017 年甚至达到了 60MB。咱们在刚刚过去的圣诞季的目标是帮它大量减肥,本文讲述了咱们实现该目标的过程。java


若是读者体验过 寻踪圣诞老人应用 的话,就会发现该应用有两大特点,「追踪器」让用户得以在全球范围内寻觅圣诞老人,另一系列在十二月提供的小游戏来帮助用户享受圣诞季🎄。android

「追踪器」是该应用的主要功能,也是最多被用户使用的功能。该功能事实上只在圣诞节前 26 小时(12 月 24 日)可用,在此期间,追踪器是最多被使用的功能。更准确地说,在 12 月的全部界面使用统计中,37% 是在 12 月 24 日 使用的,而那一天,追踪器的使用率超过了 65%ios

那么,为何这项功能如此重要呢?只有了解咱们的主要特点是什么,才能让咱们想明白,哪些是应用首次安装时最关键的功能,哪些是次要的、能够移到另外 module 中动态下发的功能,这样就使得咱们的首次安装体积变小。2017 年发布的 app 包含所有功能,其中包括所有的游戏,即便用户根本不玩这些游戏。git

是时候对寻踪圣诞老人动刀子了,咱们设立了将首次下载体积减小到仅仅 10MB 的目标😥。github

什么,为何是这个数字?由于数据显示,相比 100MB 的应用,10MB 的应用提升了 30% 的转化率。固然,尽管许多应用都在追踪转化率,寻踪圣诞老人却并非咱们追踪转化率的 app。10MB 也是一个尝试起来很难达到的目标,咱们想看看这到底是不是可行的。关于更多统计背后的信息,能够阅读 Google Play 团队 的这篇文章:后端

动态分发

读者可能据说过 Android App Bundle 这项新技术,该技术使得 Google Play 商店能够动态下发仅仅和用户设备相关的定制应用。这项技术也帮助咱们开了个好头。只需上传 AAB(Android App Bundle)来代替 APK,咱们就立刻让下载体积减小了将近 20% ,达到了 48.5MB(从 60MB)。们只不过是花了一小步的功夫,就在缩减体积方面迈进了一大步bash

若是只打算从本文中学一项技术,必定得是上传 AAB 来取代 APK。这一小改动有很大机会来节省用户的时间和金钱。网络

Google Play 是怎么实现这种瘦身的呢?这一作法可以分发针对个别设备的优化包,这么一来,相应工具就能从安装包中移除全部不适用于设备的语言资源、分辨率资源以及本地库。好比,若是你的设备设置是 fr-FR(法语),分辨率是 xxhdpi ,CPU是 arm64-v8a 架构的,下发的 APK 便只会包含必要的资源,而不会包含诸如针对西班牙语本地化的字符串之类的东西。当发现本地化字符串占用的空间有多大时,你必定会大吃一惊。

不要忘了观看 Android 开发大会 ’18 上的 ‘优化应用的体积’ 演讲来获取更多信息:

功能模块

尽管咱们有着良好的开头,却仍距离 10MB 的目标十万八千里!因此咱们开始考虑哪些功能能够被拆到动态功能模块中,用户能够经过 Play Core library 来获取所需的模块。好消息是咱们已经按逻辑分离了一大模块:游戏🎮。

因而便有了以下的计划:将每一个游戏拆分到单独的功能模块中,并只当用户第一次打开特定游戏的时候才安装。听起来很棒,不是么?尽管逻辑上游戏都分离了,但基础代码却并无分离。通过数年的功能变迁,它们已经缠缠绵绵难以分离了。应用中的库模块层层叠叠,并且处处是重复的资源。

咱们的首要工做是将其解耦和,并在游戏模块之间创建足够清晰的边界。咱们当心翼翼地分离了所有的游戏模块,经过使用新的 com.android.dynamic-feature Gradle 插件,如今每一个游戏都是彻底独立的模块了。对于那些有着相同依赖的游戏(好比 ‘Penguin Swim’ 和 ‘Elf Jetpack’ 共享了许多代码),依赖被添加到 ‘base’ 模块中,这样一来,就能够只安装一次(同时玩两个游戏)了。

功能模块的实现

正如以前说过的那样,模块迁移中占大头的工做是已有代码的从新组织,另外也有一些小的整合工做须要经过 Play Core library 来将其穿插起来。

首先是用户启动游戏时的 UX。咱们首先打开显示 logo 和游戏标题的 ‘启动页(splash screen)’ activity,过一小段时间再运行游戏。运行游戏须要的所有信息都做为 intent extras 传送到启动页了。数年来该行为都没有变化,咱们也并不打算修改这一行为。相反,咱们从中找到了动态分发功能模块的切入点。

2018 年咱们更新了启动行为,发送了四点信息:游戏标题、游戏图标、要运行的 Activity 类以及该功能模块的 ID。一旦启动页展现出来,就检查是否安装了相关模块。若是安装了,就直接运行,反之则经过 Play Core library 请求安装,并展现下载进度条:

咱们在早期测试中发现须要当心处理下载安装时的场景。咱们并不想由于在用户处于移动网络时安装功能模块,而无心中让他们花钱。为了应对这种情形,咱们在检测到当前网络是流量网络(如移动网络)时增长了确认对话框:

当链接到流量网络时的确认对话框

总体逻辑以下:

/* Copyright 2018 Google LLC.
   SPDX-License-Identifier: Apache-2.0 */

override fun onCreate(savedInstanceState: Bundle?) {
    // ... 安装

    // 游戏功能模块的 Id 
    val featureModuleId = intent.getStringExtra(...)

    if (featureModuleName in splitInstallManager.installedModules) {
        // 功能模块已经安装,直接运行
        launchTargetActivity()
    } else {
        // 功能模块没有安装,请求安装
        val mgr = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
        if (mgr.activeNetworkInfo?.isConnected == true) {
            // 有网络...
            if (mgr.isActiveNetworkMetered) {
                // TODO ...流量网络,请求用户确认
                showMeteredNetworkConfirmDialog()
            } else {
                // ...不然,直接下载
                startModuleInstall(featureModuleId)
            }
        } else {
            // 没有网络,显示错误框并退出
            onFeatureModuleLaunchFailure()
        }
    }
}
复制代码

因为 Play Core API 的缘故,startModuleInstall() 的方法看起来有些复杂。须要在安装时添加一个用于回调的 listener,而后再请求安装,以下所示:

/* Copyright 2018 Google LLC.
   SPDX-License-Identifier: Apache-2.0 */

private lateinit var splitInstallManager: SplitInstallManager
private lateinit var installListener: SplitInstallStateUpdatedListener

private fun startModuleInstall(featureModuleId: String) {
    // 显示进度条
    progressbar.isVisible = true
    progressbar.isIndeterminate = true

    // 添加 listener
    splitInstallManager.registerListener(installListener)
    
    // 发送请求,开始安装
    val request = SplitInstallRequest.newBuilder()
            .addModule(featureModuleId)
            .build()
    splitInstallManager.startInstall(request)
}
复制代码

listener 会监听到安装完成的信号,而后运行游戏。能够在 这里 找到完整代码。

成果

若是你读到这里了,必定会想知道咱们的成果如何……

Android Studio 分析 App Bundle(以及 APK)的工具至关好用,能够深刻观察每一个功能模块的下载体积。咱们能够在其中看到咱们应用的初始下载体积是 11.6MB (并无达到 10MB 的目标),总下载体积是 25.5MB。

**使用 Android Studio 中 Analyze Bundle 功能计算的下载体积**

展现模块体积对比的图表

但……这些值只展现了生成的 Android App Bundle 文件,并无计算 Google Play 动态下发(上文讨论过)节省的体积。观察特定设备下载体积最准确的方式是在 Google Play 开发者控制台 中。上传 App Bundle 后,就能够在 ‘Release Management’ -> ‘Artifact Library’ 看到特定设备的下发包体积:

计算结果是……

能够看到咱们达到了 10MB 的目标,下载体积只有 9.21MB!相比 2017 年 60MB 的应用,咱们减小了 85% 的体积! 🎉🎆

高画质的实际截图

普惠众生

但愿本文展现了迁移到 App Bundle 能够带给用户的巨大收益。尽管分离模块并非什么举手之劳,但好的代码实践诸如高内聚低耦合也会收益良多。

关于上面的数字还有一小点要注意的是,其中也有咱们使用的其余体积压缩技术的功劳,包括 asset 压缩和迁移到 R8。咱们会在下篇文章中讨论这些。

  • 读者可能会好奇为何是 26 个小时而不是 24?这是由于国际日期变动线 并非一条直线。基里巴斯的时区是 UTC+14,这意味着它和豪兰岛和贝克岛(UTC-12 时区)间有 26 小时的时差。

若是发现译文存在错误或其余须要改进的地方,欢迎到 掘金翻译计划 对译文进行修改并 PR,也可得到相应奖励积分。文章开头的 本文永久连接 即为本文在 GitHub 上的 MarkDown 连接。


掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 AndroidiOS前端后端区块链产品设计人工智能等领域,想要查看更多优质译文请持续关注 掘金翻译计划官方微博知乎专栏

相关文章
相关标签/搜索