如果您将应用发布到 Google Play,则应构建并上传 Android 应用包。这样做后,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,进展如何?”,而只需说“蓝色 APK 的进展如何?”。
将所有公共代码和资源放入库项目中
无论您是在修改现有的 Android 应用程序还是从头开始创建应用程序,这都是您应该对代码库执行的第一件事,并且是迄今为止最重要的一件事。库项目中的所有内容只需要更新一次(例如语言本地化字符串、颜色主题、修复共享代码中的错误),从而缩短了开发时间并降低了可能轻松避免的错误的可能性。
注意:虽然有关如何创建和包含库项目的实现细节超出了本课程的范围,但您可以通过阅读创建 Android 库来快速上手。
如果您要将现有应用程序转换为使用多个 APK 支持,请仔细检查您的代码库,查找所有不会跨 APK 更改的本地化字符串文件、值列表、主题颜色、菜单图标和布局,并将它们全部放入库项目中。不会发生太大变化的代码也应该放入库项目中。您可能会发现自己扩展这些类以从 APK 添加一两个方法。
另一方面,如果您要从头开始创建应用程序,请尽可能先在库项目中编写代码,然后仅在必要时将其移至单个 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 不必重新实现“通用”任务,例如初始化分析、运行许可证检查以及任何其他不会从 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 清单文件可能如下所示
蓝色
<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 屏幕上支持纸杯蛋糕的 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 输出时,请确保您没有为 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 正在定位目标设备。恭喜,您完成了!