如果您将应用发布到 Google Play,应构建并上传 Android App Bundle。这样,Google Play 会自动为每位用户的设备配置生成并提供优化的 APK,因此用户只需下载运行应用所需的代码和资源。如果您不发布到 Google Play,发布多个 APK 会很有用,但您必须自行构建、签名和管理每个 APK。
在开发 Android 应用以利用 Google Play 上的多个 APK 时,从一开始就采纳一些良好实践,并避免在开发过程中后期出现不必要的麻烦非常重要。本课程将向您展示如何为您的应用创建多个 APK,每个 APK 涵盖不同类别的屏幕尺寸。您还将获得一些必要的工具,以尽可能轻松地维护多 APK 代码库。
确认您需要多个 APK
在尝试创建适用于多种 Android 设备尺寸的应用时,您自然希望您的应用能够利用更大设备上的所有可用空间,同时又不牺牲在小屏幕上的兼容性或可用性。乍一看,多 APK 支持似乎是最佳解决方案,但情况往往并非如此。多 APK 开发者指南的改用单个 APK 部分包含了一些关于如何使用单个 APK 实现此目的的有用信息,包括使用我们的支持库。您还应该阅读支持多种屏幕的指南,甚至还有一个可以通过 Android SDK 下载的支持库,它允许您在 Honeycomb 之前的设备上使用片段(使单个 APK 中的多屏幕支持变得容易得多)。
如果可能,将您的应用限制为单个 APK 具有多项优势,包括
- 发布和测试更简单
- 只有一个代码库需要维护
- 您的应用可以适应设备配置更改
- 跨设备应用恢复即时生效
- 您无需担心市场偏好、从一个 APK 到下一个 APK 的“升级”行为,或者哪个 APK 适用于哪类设备
本课程的其余部分假定您已研究过该主题,认真吸收了链接资源中的材料,并确定多个 APK 是您应用的正确途径。
绘制您的需求图
首先创建一个简单的图表,以快速确定您需要多少个 APK,以及每个 APK 涵盖哪些屏幕尺寸。幸运的是,您可以轻松快速地绘制出您的需求,并作为以后的参考。从一行单元格开始,这些单元格代表 Android 平台上可用的各种屏幕尺寸。
small | normal | large | xlarge |
现在只需在图表中着色,使每种颜色代表一个 APK。以下是您如何将每个 APK 应用到特定屏幕尺寸范围的一个示例。
small | normal | large | xlarge |
根据您的需求,您也可以有两个 APK,“小型及其他”或“超大型及其他”。在图表中着色还可以简化团队内部沟通——无论它涵盖多少种不同的屏幕类型,您现在只需将每个 APK 称为“蓝色”、“绿色”或“红色”。
将所有通用代码和资源放入库项目
无论您是修改现有 Android 应用还是从头开始创建,这都是您应该对代码库做的第一件事,也是迄今为止最重要的事情。所有进入库项目的内容只需更新一次(例如语言本地化字符串、颜色主题、共享代码中修复的错误),这可以缩短您的开发时间,并减少本可以轻松避免的错误的可能性。
注意:虽然如何创建和包含库项目的实现细节超出了本课程的范围,但您可以通过阅读创建 Android 库来快速掌握。
如果您正在将现有应用转换为使用多 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 就不必重新实现“通用”任务,例如初始化 Analytics、运行许可检查以及其他从 APK 到 APK 变化不大的初始化过程。
调整清单
当用户通过 Google Play 下载使用多个 APK 的应用时,会根据两条简单规则选择要使用的正确 APK:
- 清单必须表明该特定 APK 符合条件
- 在符合条件的 APK 中,版本号最高的获胜
举例来说,让我们以上述多个 APK 的集合为例,假设每个 APK 都已设置为支持所有大于其“目标”屏幕尺寸的屏幕尺寸。单独来看,每个 APK 的可能范围将如下所示:
small | normal | large | xlarge |
small | normal | large | xlarge |
small | normal | large | xlarge |
然而,通过使用“最高版本号获胜”规则,如果我们将每个 APK 中的 versionCode 属性设置为 red ≥ green ≥ blue,则图表将有效缩减为:
small | normal | large | xlarge |
现在,让我们进一步假设红色 APK 有一些其他两个 APK 所没有的要求。Google Play 上的过滤条件页面是 Android 开发者指南的一部分,其中包含了一系列可能导致此问题的原因。举例来说,我们假设红色 APK 需要一个前置摄像头。事实上,红色 APK 的全部目的是利用额外的屏幕空间通过前置摄像头做一些有趣的事情。但是,事实证明,并非所有 xlarge 设备都配备前置摄像头!太可怕了!
幸运的是,如果用户从这类设备浏览 Google Play,Google Play 会查看清单,发现红色 APK 将前置摄像头列为一项要求,并默默地忽略它,因为它已确定红色 APK 和该设备并非天作之合。然后它会看到绿色 APK 不仅与 xlarge 设备兼容,而且也不关心是否有前置摄像头!用户仍然可以从 Google Play 下载该应用,因为尽管发生了前置摄像头意外情况,但仍然有一个 APK 支持该特定屏幕尺寸。
为了使所有 APK 保持在独立的“轨道”上,拥有一个良好的版本代码方案非常重要。推荐的方案可在我们的开发者指南的版本代码部分找到。由于示例 APK 集只处理 3 个可能维度中的一个,因此将每个 APK 间隔 1000 并从那里递增就足够了。这可能看起来像
蓝色:1001、1002、1003、1004...
绿色:2001、2002、2003、2004...
红色:3001、3002、3003、3004...
综上所述,您的 Android Manifests 可能如下所示:
蓝色
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1001" android:versionName="1.0" package="com.example.foo"> <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="2001" android:versionName="1.0" package="com.example.foo"> <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="3001" android:versionName="1.0" package="com.example.foo"> <supports-screens android:smallScreens="false" android:normalScreens="false" android:largeScreens="false" android:xlargeScreens="true" /> ...
请注意,从技术上讲,多个 APK 都可以使用 supports-screens 标签或 compatible-screens 标签。通常首选 supports-screens,并且通常在同一个清单中使用这两个标签是一个非常糟糕的主意。它会使事情变得不必要的复杂,并增加出错的机会。另请注意,清单明确设置了每个屏幕尺寸的值,而不是利用默认值(小型和普通默认情况下始终为 true)。这可以为您省去后顾之忧。例如,目标 SDK 小于 9 的清单将自动将 xlarge 设置为 false,因为该尺寸当时尚不存在。所以,请明确指定!
检查您的发布前清单
在上传到 Google Play 之前,请仔细检查以下各项。请记住,这些内容专门与多个 APK 相关,绝不代表所有上传到 Google Play 的应用的完整清单。
- 所有 APK 必须具有相同的软件包名称
- 所有 APK 必须使用相同的证书进行签名
- 在清单中将您的 APK 想要支持的每个屏幕尺寸设置为 true。将您想要避免的每个屏幕尺寸设置为 false
- 仔细检查您的清单过滤器是否存在冲突信息(只支持 XLARGE 屏幕上的 Cupcake 的 APK 将不会被任何人看到)
- 每个 APK 的清单必须在至少一个支持的屏幕、OpenGL 纹理或平台版本中是唯一的
- 尝试在至少一台设备上测试每个 APK。如果不行,您的开发机器上就有一个业内最可定制的设备模拟器。尽情测试吧!
在推向市场之前,检查编译后的 APK 也很有价值,以确保没有任何可能在 Google Play 上隐藏您的应用的意外情况。使用“aapt”工具实际上非常简单。Aapt(Android Asset Packaging Tool)是创建和打包 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 的功能要求。由于大多数(如果不是全部)xlarge 设备是没有电话硬件的平板电脑,因此 Google Play 在这些情况下会过滤掉此 APK,直到未来的设备出现,这些设备既大到足以报告为 xlarge 屏幕尺寸,又拥有电话硬件。
幸运的是,通过将以下内容添加到您的清单中可以轻松解决此问题:
<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 正在定位预期的设备。
有关在 Google Play 上发布多个 APK 的更多信息,请阅读多 APK 支持。