如果您将应用发布到 Google Play,您应构建并上传 Android App Bundle 文件。执行此操作后,Google Play 会针对每个用户的设备配置自动生成并提供经过优化的 APK,以便他们仅下载运行应用所需的代码和资源。如果您不将应用发布到 Google Play,则发布多个 APK 非常有用,但您必须自行构建和管理每个 APK 并为其签名。
若要开发 Android 应用以在 Google Play 上利用多个 APK,您需要从一开始就采取一些好的做法,以免在开发过程的后期遇到不必要的麻烦。这节课向您介绍如何创建应用的多个 APK,并使每个 APK 支持一个不同的屏幕尺寸。此外,我们还会向您提供一些必要的工具,帮助您尽可能轻松地维护多 APK 代码库。
确认您需要多个 APK
当您尝试创建可在各种现有 Android 设备上运行的应用时,您自然希望应用在每台设备上都能达到最佳效果。您希望能够利用大屏幕的空间,同时仍能适应小屏幕,希望能够利用最新设备上提供的新 Android API 功能或视觉纹理,同时又不放弃旧设备。乍一看,支持多 APK 似乎就是最佳解决方案,但事实往往并非如此。多 APK 指南的改用单个 APK 部分提供了一些有关如何使用单个 APK 实现该目标的有用信息,包括如何使用我们的支持库以及整个 Android 开发者指南中的资源链接。
如果您可以将应用限制为单个 APK,则这种做法有以下几点优势:
- 发布和测试更加轻松
- 只需要维护一个代码库
- 您的应用可以适应设备配置的变化
- 可以跨设备执行应用恢复
- 您不必担心市场偏好、从一个 APK“升级”到下一个 APK 的行为,或者哪个 APK 与哪类设备相匹配
本节课的后续内容假设您已经研究了这个主题,认真学习了链接资源中的资料,并确定创建多个 APK 是适合您应用的正确方法。
制作您的需求图表
首先,您可以创建一个简单的图表来快速确定您需要多少个 APK,以及每个 APK 所支持的屏幕尺寸。幸运的是,您可以快速轻松地制作出您的需求图表,以便于以后参考。假设您希望将 APK 拆分为两个维度,即 API 和屏幕尺寸。创建包含所有可能的值对的行列表格,并在某些“blob”中着色,每种颜色代表一个 APK。
3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | + | |
small | |||||||||||
normal | |||||||||||
large | |||||||||||
xlarge |
以上示例中包含 4 个 APK。蓝色 APK 适用于所有小屏幕/标准屏幕设备,绿色 APK 适用于大屏幕设备,红色 APK 适用于特大屏幕设备,并且这些 APK 的 API 范围均为 3-10。紫色 APK 是一种特殊情况,因为它适用于所有屏幕尺寸,但仅适用于 11 及更高级别的 API。更重要的是,只需要看一眼这个图表,马上就可以知道哪个 APK 支持任何指定的 API/屏幕尺寸组合。此外,每个 APK 还有了简洁的代号,因为比起询问团队成员“我们是否已针对 Xoom 测试了 3-10 xlarge APK?”,询问“我们是否已针对 Xoom 测试了红色 APK?”要简单多了。打印这张图表并将其分发给处理代码库的每个人。这样工作就轻松多了。
将所有通用代码和资源放在一个库项目中
无论您是要修改现有的 Android 应用还是从头开始创建 Android 应用,这都是您应该执行的第一项也是最重要的一项代码库操作。放入库项目中的所有内容都只需要更新一次(例如经过本地化的字符串、颜色主题背景、共享代码中修复的问题),这样可以缩短开发时间并减少发生原本很容易避免的错误的可能性。
注意:尽管有关如何创建和包含库项目的实现详情不在本课的讨论范围内,但阅读创建 Android 库有助于您跟上学习进度。
如果要将现有应用转换为支持多 APK,请搜索代码库,找出在不同 APK 之间通用的所有已本地化的字符串文件、值列表、主题背景颜色、菜单图标和布局,并将它们全部放在库项目中。不会发生太大变化的代码也应该放在库项目中。您可能会发现自己需要扩展这些类,以便将一两个方法从一个 APK 添加到另一个 APK。
另一方面,如果您要从头开始创建应用,请尽可能先尝试在库项目中编写代码,然后在必要时才将其移到单个 APK 中。从长远来看,比起将 Blob 逐个添加到各个 APK 中,然后过几个月再查看能否在不造成任何问题的前提下将该 Blob 移到库中,这种做法更易于管理。
创建新的 APK 项目
您应该为要发布的每个 APK 单独创建一个 Android 项目。为便于管理,请将库项目和所有相关的 APK 项目放在同一个父文件夹下。另外请注意,这些 APK 必须具有相同的软件包名称,但可以与库的软件包名称不同。如果您有 3 个遵循上述方案的 APK,您的根目录可能如下所示:
alexlucas:~/code/multi-apks-root$ ls foo-blue foo-green foo-lib foo-purple foo-red
项目创建完毕后,请将库项目作为引用添加到各个 APK 项目中。如果可能,请在库项目中定义启动 Activity,然后在 APK 项目中扩展该 Activity。在库项目中定义启动 Activity,可让您将应用的所有初始化过程放在一个位置执行,这样每个单独的 APK 就不必重新实现“通用”任务,比如初始化 Analytics、运行许可检查,以及任何其他在不同 APK 之间没有太大差异的初始化过程。
调整清单
当用户通过 Google Play 下载使用多个 APK 的应用时,系统会使用 2 个简单的规则来选择正确的 APK:
- 清单必须显示特定的 APK 符合条件
- 在符合条件的 APK 中,选择版本最高的一个
我们以上文中的一组多 APK 为例,假设每个 APK 都被设置为支持所有大于其“目标”屏幕尺寸的屏幕尺寸。让我们来看看之前的示例图表:
3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | + | |
small | |||||||||||
normal | |||||||||||
large | |||||||||||
xlarge |
由于支持范围可以重叠,因此我们可以这样描述每个 APK 支持的范围:
- 蓝色 APK 支持所有屏幕,minSDK 为 3。
- 绿色 APK 支持大屏幕及更大的屏幕,minSDK 为 3。
- 红色 APK 支持特大屏幕(通常为平板电脑),minSDK 为 9。
- 紫色 APK 支持所有屏幕,minSDK 为 11。
请注意,这些规则有很多重叠。例如,可以想象,一个具有 API 11 的特大屏幕设备可以运行 4 个指定 APK 中的任意一个。但是,通过使用“选择版本最高的一个”规则,我们可以设置以下优先顺序:
紫色 ≥ 红色 ≥ 绿色 ≥ 蓝色
为什么允许所有重叠?让我们假设紫色 APK 有一些另外 3 种颜色的 APK 所没有的要求。“Android 开发者指南”中的 Google Play 上的过滤器页面上列出了所有可能的问题根源。举例来说,假设紫色 APK 需要前置摄像头。事实上,紫色 APK 的全部意义就在于通过前置摄像头使用娱乐内容!但事实是,有些 API 11+ 设备根本没有前置摄像头!这太糟糕了!
幸运的是,如果用户在这样的设备上浏览 Google Play,Google Play 会查看清单,看到紫色 APK 列出了前置摄像头的要求并直接忽略它,因为 Google Play 已确定紫色 APK 和该设备不匹配。然后,Google Play 会发现红色 APK 不仅可以兼容特大屏幕设备,而且不在乎是否有前置摄像头!用户仍然可以从 Google Play 下载该应用,因为尽管发生了前置摄像头的小意外,但仍然有 APK 可以支持该特定 API 级别。
为了便于区分各个 APK,必须制定有效的版本代码方案。您可以在开发者指南中的版本代码部分找到推荐的方案。您可以通读这部分内容,但基本要点就是对于这组 APK,我们将用两位数代表 minSDK,两位数代表最小/最大屏幕尺寸,三位数代表版本号。这样一来,当设备升级到新版本 Android 时(比如,从 10 升级到 11),任何现在符合条件且优先于当前安装版本的 APK 都会被设备视为“升级”。如果将该版本号方案应用于这组示例 APK,结果可能如下所示:
蓝色:0304001, 0304002, 0304003...
绿色:0334001, 0334002, 0334003
红色:0344001, 0344002, 0344003...
紫色:1104001, 1104002, 1104003...
综上所述,您的 Android 清单内容可能类似于这样:
蓝色:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="0304001" android:versionName="1.0" package="com.example.foo"> <uses-sdk android:minSdkVersion="3" /> <supports-screens android:smallScreens="true" android:normalScreens="true" android:largeScreens="true" android:xlargeScreens="true" /> ...
绿色:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="0334001" android:versionName="1.0" package="com.example.foo"> <uses-sdk android:minSdkVersion="3" /> <supports-screens android:smallScreens="false" android:normalScreens="false" android:largeScreens="true" android:xlargeScreens="true" /> ...
红色:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="0344001" android:versionName="1.0" package="com.example.foo"> <uses-sdk android:minSdkVersion="3" /> <supports-screens android:smallScreens="false" android:normalScreens="false" android:largeScreens="false" android:xlargeScreens="true" /> ...
紫色:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1104001" android:versionName="1.0" package="com.example.foo"> <uses-sdk android:minSdkVersion="11" /> <supports-screens android:smallScreens="true" android:normalScreens="true" android:largeScreens="true" android:xlargeScreens="true" /> ...
请注意,从技术上讲,多个 APK 将与 supports-screens 标记或 compatible-screens 标记配合使用。通常来说,首选 supports-screens,并且通常不建议在一个清单中同时使用这两个标记。这会无谓地增加复杂性,并增大出错的几率。此外,还需要注意的是,清单会明确设置每个屏幕尺寸的值,而不是采用默认值(默认情况下,“small”和“normal”始终为 true)。这可以为您省掉今后的麻烦。例如,目标 SDK < 9 的清单会自动将“xlarge”设置为 false,因为该尺寸尚不存在。所以请明确设置值!
检查发布前核对清单
在上传到 Google Play 之前,请仔细检查以下各项内容。请注意,这些内容都是与多个 APK 相关的,并不能代表上传到 Google Play 的所有应用的完整核对清单。
- 所有 APK 必须具有相同的软件包名称。
- 所有 APK 必须使用相同的证书签名。
- 如果 APK 在平台版本上出现重叠,则 minSdkVersion 较高的 APK 应具有更高的版本号。
- 您希望 APK 支持的每种屏幕尺寸都在清单中被设为 true,而您不希望 APK 支持的屏幕尺寸则应设为 false。
- 仔细检查您的清单过滤器是否有冲突的信息(仅在特大屏幕上支持 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: 'xlarge' supports-any-density: 'true' locales: '--_--' densities: '120' '160' '240'
检查 aapt 输出时,务必要检查 supports-screens 和 compatible-screens 的值是否存在冲突,以及您在清单中设置的权限是否导致添加了意外的“uses-feature”值。上例中的 APK 将对大多数设备(即使不是所有设备)不可见。
为什么?添加必要的 SEND_SMS 权限时,就隐式添加了 android.hardware.telephony 功能要求。由于大多数(如果不是所有)特大屏幕设备都是没有电话硬件的平板电脑,因此 Google Play 会在这些情况下过滤掉此 APK,直到未来出现不仅满足特大屏幕尺寸要求并且拥有电话硬件的设备。
幸运的是,通过将以下内容添加到清单可轻松解决此问题:
<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 定位到正确的设备。恭喜您大功告成了!