如果您将应用发布到 Google Play,应构建并上传一个 Android App Bundle。这样,Google Play 会自动为每位用户的设备配置生成并提供优化的 APK,因此他们只需下载运行应用所需的代码和资源。如果您不发布到 Google Play,发布多个 APK 会很有用,但您必须自行构建、签名和管理每个 APK。
在开发 Android 应用程序以利用 Google Play 上的多个 APK 时,从一开始就采用一些良好实践非常重要,以防止在开发过程中出现不必要的麻烦。本课程将向您展示如何创建应用程序的多个 APK,每个 APK 覆盖稍有不同的 API 级别范围。您还将获得一些必要的工具,使维护多个 APK 代码库尽可能地轻松。
确认您需要多个 APK
当尝试创建一个可在 Android 平台多个代际上运行的应用程序时,您自然希望您的应用程序能够利用新设备上的新功能,而又不牺牲向后兼容性。乍一看,多 APK 支持似乎是最佳解决方案,但这通常并非如此。多 APK 开发者指南的改用单个 APK 部分包含一些有关如何使用单个 APK 实现此目的的有用信息,包括使用我们的支持库。您还可以通过本文学习如何在单个 APK 中编写仅在特定 API 级别运行的代码,而无需诉诸计算成本高昂的技术(如反射)。
如果能够做到,将您的应用程序限制在一个 APK 中具有以下几个优点:
- 发布和测试更简单
- 只需维护一个代码库
- 您的应用程序可以适应设备配置更改
- 跨设备的应用恢复功能可以直接使用
- 您无需担心市场偏好、从一个 APK “升级”到下一个 APK 的行为,或者哪个 APK 适合哪类设备
本课程的其余部分假定您已研究了该主题,认真吸收了链接资源中的材料,并确定多个 APK 是您应用程序的正确路径。
制定您的需求图
首先创建一个简单的图表,以便快速确定您需要多少个 APK,以及每个 APK 覆盖的 API 范围。为方便参考,Android 开发者网站的平台版本页面提供了有关运行特定版本 Android 平台的活跃设备相对数量的数据。此外,虽然一开始听起来很容易,但跟踪每个 APK 将要针对的 API 级别集合会很快变得困难,特别是如果存在一些重叠(通常是这样)。幸运的是,快速、轻松地绘制出您的需求图,并为以后提供便捷的参考是很容易的。
为了创建您的多 APK 图表,首先创建一行单元格,代表 Android 平台的各种 API 级别。在末尾添加一个额外的单元格,代表未来的 Android 版本。
3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | + |
现在只需为图表着色,使每种颜色代表一个 APK。这是一个示例,说明您如何将每个 APK 应用到特定的 API 级别范围。
3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | + |
创建此图表后,分发给您的团队。这样,您的项目团队沟通立即变得更简单了,因为您无需再问“针对 API 级别 3 到 6 的 APK,就是 Android 1.x 那个,进展如何?”您只需说“蓝色 APK 进展如何?”
将所有通用代码和资源放入库项目中
无论您是修改现有 Android 应用程序还是从头开始创建,这都是您应该对代码库做的第一件事,也是迄今为止最重要的一件事。库项目中的所有内容只需更新一次(例如本地化字符串、颜色主题、共享代码中修复的错误),这可以缩短您的开发时间并减少本可轻易避免的错误的可能性。
注意:虽然创建和包含库项目的具体实现细节超出了本课程的范围,但您可以通过阅读创建 Android 库来快速了解。
如果您正在将现有应用程序转换为使用多个 APK 支持,请仔细检查您的代码库,查找每个本地化字符串文件、值列表、主题颜色、菜单图标和在 APK 之间不会改变的布局,并将它们全部放入库项目中。不会经常更改的代码也应该放入库项目中。您可能会发现自己会扩展这些类,以便在 APK 之间添加一两个方法。
另一方面,如果您是从头开始创建应用程序,请尽可能多地首先在库项目中编写代码,然后仅在必要时才将其移至单个 APK。从长远来看,这比将其添加到第一个、然后是第二个、然后是第三个,然后几个月后试图弄清楚这团代码是否可以移到库部分而不会弄乱任何东西要容易得多。
创建新的 APK 项目
您要发布的每个 APK 都应该有一个单独的 Android 项目。为了便于组织,请将库项目和所有相关的 APK 项目放在同一父文件夹下。另请记住,每个 APK 都必须具有相同的包名,尽管它们不一定需要与库共享包名。如果您的 3 个 APK 遵循前面描述的方案,则您的根目录可能如下所示:
alexlucas:~/code/multi-apks-root$ ls foo-blue foo-green foo-lib foo-red
项目创建完成后,将库项目作为引用添加到每个 APK 项目中。如果可能,请在库项目中定义您的启动 Activity,并在您的 APK 项目中扩展该 Activity。在库项目中定义启动 Activity 让您有机会将所有应用程序初始化放在一个地方,这样每个单独的 APK 就不必重新实现“通用”任务,例如初始化 Analytics、运行许可检查以及任何其他在 APK 之间变化不大的初始化过程。
调整清单
当用户通过 Google Play 下载使用多个 APK 的应用程序时,将使用两个简单的规则选择要使用的正确 APK:
- 清单必须表明该特定 APK 符合条件
- 在符合条件的 APK 中,版本号最高者胜出
举例来说,让我们以上面描述的多个 APK 集为例,并假设我们没有为任何 APK 设置最大 API 级别。单独来看,每个 APK 的可能范围如下所示:
3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | + |
3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | + |
3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | + |
因为要求具有更高 minSdkVersion 的 APK 也必须具有更高的版本代码,所以我们知道就 versionCode 值而言,红色 ≥ 绿色 ≥ 蓝色。因此,我们可以有效地将图表折叠成如下所示:
3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | + |
现在,我们进一步假设红色 APK 有一些其他两个 APK 没有的要求。Android 开发者指南的Google Play 上的过滤器页面列出了所有可能的“罪魁祸首”。为了举例说明,我们假设红色需要一个前置摄像头。事实上,红色 APK 的全部目的就是将前置摄像头与 API 11 中添加的全新功能结合起来。但事实证明,并非所有支持 API 11 的设备都有前置摄像头!真是可怕!
幸运的是,如果用户正在从此类设备浏览 Google Play,Google Play 会查看清单,看到红色列出前置摄像头作为要求,并悄悄忽略它,因为它已确定红色和该设备并非天作之合。然后它会看到绿色不仅与 API 11 设备向前兼容(因为没有定义 maxSdkVersion),而且也不关心是否有前置摄像头!用户仍然可以从 Google Play 下载该应用程序,因为尽管发生了前置摄像头事故,但仍然有一个 APK 支持该特定 API 级别。
为了使所有 APK 保持在不同的“轨道”上,拥有一个良好的版本代码方案非常重要。建议的方案可以在我们的开发者指南的版本代码区域找到。由于示例 APK 集仅涉及 3 个可能维度中的一个,因此将每个 APK 分隔 1000 就足够了,将前几位数字设置为该特定 APK 的 minSdkVersion,然后从那里递增。这可能看起来像:
蓝色: 03001, 03002, 03003, 03004...
绿色: 07001, 07002, 07003, 07004...
红色:11001, 11002, 11003, 11004...
综合所有这些,您的 Android Manifests 可能如下所示:
蓝色
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="03001" android:versionName="1.0" package="com.example.foo"> <uses-sdk android:minSdkVersion="3" /> ...
绿色
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="07001" android:versionName="1.0" package="com.example.foo"> <uses-sdk android:minSdkVersion="7" /> ...
红色
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="11001" android:versionName="1.0" package="com.example.foo"> <uses-sdk android:minSdkVersion="11" /> ...
审查您的发布前核对清单
在上传到 Google Play 之前,请仔细检查以下各项。请记住,这些专门针对多个 APK,绝不代表所有上传到 Google Play 的应用程序的完整核对清单。
- 所有 APK 都必须具有相同的包名
- 所有 APK 都必须使用相同的证书签名
- 如果 APK 在平台版本上重叠,则 minSdkVersion 更高的 APK 必须具有更高的版本代码
- 仔细检查您的清单过滤器是否存在冲突信息(只支持 XLARGE 屏幕上的 cupcake 的 APK 将不会被任何人看到)
- 每个 APK 的清单必须在支持的屏幕、OpenGL 纹理或平台版本中至少有一个是唯一的
- 尝试在至少一个设备上测试每个 APK。如果不行,您的开发机器上有一个业务中最可定制的设备模拟器。尽情发挥吧!
在推向市场之前,检查编译后的 APK 也很有价值,以确保没有可能在 Google Play 上隐藏您的应用程序的意外情况。使用“aapt”工具实际上非常简单。Aapt(Android 资产打包工具)是创建和打包 Android 应用程序构建过程的一部分,也是检查它们的非常方便的工具。
>aapt dump badging package: name='com.example.hello' versionCode='1' versionName='1.0' sdkVersion:'11' uses-permission:'android.permission.SEND_SMS' application-label:'Hello' application-icon-120:'res/drawable-ldpi/icon.png' application-icon-160:'res/drawable-mdpi/icon.png' application-icon-240:'res/drawable-hdpi/icon.png' application: label='Hello' icon='res/drawable-mdpi/icon.png' launchable-activity: name='com.example.hello.HelloActivity' label='Hello' icon='' uses-feature:'android.hardware.telephony' uses-feature:'android.hardware.touchscreen' main supports-screens: 'small' 'normal' 'large' 'xlarge' supports-any-density: 'true' locales: '--_--' densities: '120' '160' '240'
当您检查 aapt 输出时,请务必检查您没有 for supports-screens 和 compatible-screens 的冲突值,并且没有由于您在清单中设置的权限而添加的意外“uses-feature”值。在上面的示例中,该 APK 将不会被很多设备看到。
为什么?通过添加必需的权限 SEND_SMS,隐式添加了 android.hardware.telephony 的功能要求。由于 API 11 是 Honeycomb(专门为平板电脑优化的 Android 版本),并且没有 Honeycomb 设备内置电话硬件,因此 Google Play 在所有情况下都会过滤掉此 APK,直到未来出现 API 级别更高且具有电话硬件的设备。
幸运的是,通过在清单中添加以下内容可以轻松修复此问题:
<uses-feature android:name="android.hardware.telephony" android:required="false" />
android.hardware.touchscreen
要求也被隐式添加。如果您希望您的 APK 在非触屏电视上可见,您应该在清单中添加以下内容:
<uses-feature android:name="android.hardware.touchscreen" android:required="false" />
完成发布前核对清单后,将您的 APK 上传到 Google Play。应用程序可能需要一段时间才能在 Google Play 上显示,但显示后,请进行最后一次检查。将应用程序下载到您可能拥有的任何测试设备上,以确保 APK 正在定位预期的设备。恭喜,您已完成!