如果你将你的应用发布到 Google Play,你应该构建并上传一个 Android 应用包。这样做时,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 涵盖哪些屏幕尺寸。幸运的是,您可以快速、轻松地规划需求,并方便以后参考。假设您想根据 API 和屏幕尺寸两个维度拆分 APK。创建一个表格,为每个可能的数值对添加一行和一列,并用颜色填充一些“块”,每种颜色代表一个 APK。
3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | + | |
小型 | |||||||||||
普通 | |||||||||||
大型 | |||||||||||
超大型 |
以上是一个包含四个 APK 的示例。蓝色代表所有小型/普通屏幕设备,绿色代表大型屏幕设备,红色代表超大型屏幕设备,所有这些设备的 API 范围都是 3-10。紫色是一种特殊情况,因为它适用于所有屏幕尺寸,但仅适用于 API 11 及更高版本。更重要的是,只需一眼就能从该图表中立即知道哪个 APK 涵盖任何给定的 API/屏幕尺寸组合。此外,您还可以为每个 APK 起一个别致的代号,因为向您的助手询问“我们是否在?上测试了红色”比询问“我们是否针对 Xoom 测试了 3 到 10 的超大型 APK”容易得多。打印此图表并将其交给参与代码库工作的每一个人。生活变得轻松多了。
将所有公共代码和资源放在库项目中
无论您是修改现有的 Android 应用程序还是从头开始创建应用程序,这都是您应该对代码库执行的第一件事,也是最重要的一件事。库项目中的所有内容只需要更新一次(例如语言本地化字符串、颜色主题、共享代码中修复的错误),这可以缩短您的开发时间并降低发生本可轻松避免的错误的可能性。
注意:虽然关于如何创建和包含库项目的实现细节不在本课的讨论范围之内,但您可以阅读创建 Android 库来快速入门。
如果您要将现有应用程序转换为使用多个 APK 支持,请仔细检查您的代码库,查找每个本地化字符串文件、值列表、主题颜色、菜单图标和布局,这些内容不会跨 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-purple foo-red
创建项目后,将库项目添加为对每个 APK 项目的引用。如果可能,在库项目中定义您的起始 Activity,并在您的 APK 项目中扩展该 Activity。在库项目中定义起始 Activity 使您可以有机会将所有应用程序初始化放在一个地方,这样每个单独的 APK 都无需重新实现“通用”任务,例如初始化分析、运行许可证检查以及任何其他不会从一个 APK 到另一个 APK 发生太大变化的初始化过程。
调整清单
当用户通过 Google Play 下载使用多个 APK 的应用程序时,将使用两条简单的规则来选择要使用的正确 APK
- 清单必须显示该特定 APK 符合条件。
- 在符合条件的 APK 中,版本号最高的获胜。
例如,让我们来看一下前面描述的那组多个 APK,并假设每个 APK 都已设置为支持大于其“目标”屏幕尺寸的所有屏幕尺寸。让我们看看前面的示例图表
3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | + | |
小型 | |||||||||||
普通 | |||||||||||
大型 | |||||||||||
超大型 |
由于覆盖范围重叠是可以的,因此我们可以这样描述每个 APK 涵盖的区域
- 蓝色涵盖所有屏幕,minSDK 3。
- 绿色涵盖大型屏幕及更高版本,minSDK 3。
- 红色涵盖超大型屏幕(通常是平板电脑),minSDK 为 9。
- 紫色涵盖所有屏幕,minSDK 为 11。
请注意,这些规则存在大量的重叠。例如,具有 API 11 的超大型设备可以运行指定的 4 个 APK 中的任何一个。但是,通过使用“版本号最高的获胜”规则,我们可以设置如下优先级:
紫色 ≥ 红色 ≥ 绿色 ≥ 蓝色
为什么允许所有重叠?让我们假设紫色 APK 有一些其他两个 APK 不具备的要求。Android 开发人员指南的Google Play 上的过滤器页面列出了所有可能的罪魁祸首。例如,让我们假设紫色需要一个前置摄像头。事实上,紫色的全部意义就是使用前置摄像头来做一些有趣的事情!但是,事实证明,并非所有 API 11 以上的设备都具有前置摄像头!太可怕了!
幸运的是,如果用户从这样的设备浏览 Google Play,Google Play 将查看清单,发现紫色将前置摄像头列为要求,并将悄然忽略它,因为它已确定紫色和该设备并非天生一对。然后,它会发现红色不仅与超大型设备兼容,而且不关心是否存在前置摄像头!用户仍然可以从 Google Play 下载该应用程序,因为尽管发生了前置摄像头的问题,但仍然有一个 APK 支持该特定 API 级别。
为了将所有 APK 保留在单独的“轨道”上,拥有良好的版本代码方案非常重要。推荐的方案可以在我们的开发者指南的版本代码部分找到。值得阅读整个部分,但对于这组 APK,我们将使用两位数字表示 minSDK,两位数字表示最小/最大屏幕尺寸,三位数字表示内部版本号。这样,当设备升级到新版本的 Android 时(例如,从 10 升级到 11),任何现在符合条件且优于当前已安装 APK 的 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 是首选的,而且同时使用两者通常是一个非常糟糕的主意——它会使事情变得不必要地复杂,并增加出错的可能性。另请注意,清单明确设置了每个屏幕尺寸的值,而不是利用默认值(小型和普通始终默认为 true)。这可以避免以后出现问题——例如,目标 SDK 小于 9 的清单会自动将 xlarge 设置为 false,因为该尺寸当时还不存在。因此,请明确说明!
查看您的预发布清单
在上传到 Google Play 之前,请仔细检查以下项目。请记住,这些项目与多个 APK 特别相关,绝不代表上传到 Google Play 的所有应用程序的完整清单。
- 所有 APK 必须具有相同的包名。
- 所有 APK 必须使用相同的证书签名。
- 如果 APK 在平台版本上重叠,则 minSdkVersion 更高的 APK 必须具有更高的版本代码。
- 您希望 APK 支持的每个屏幕尺寸,都应在清单中设置为 true。您希望 APK 避免的每个屏幕尺寸,都应设置为 false。
- 仔细检查清单过滤器中是否存在冲突信息(仅支持杯子蛋糕的超大型屏幕 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 针对的是目标设备。恭喜,您完成了!