为不同的屏幕尺寸创建多个 APK

如果你将应用发布到 Google Play,则应构建并上传Android 应用包。这样做后,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 平台上可用的各种屏幕尺寸。

小型 普通 大型 超大型

现在只需为图表着色,每个颜色代表一个 APK。以下是一个示例,说明如何将每个 APK 应用于特定范围的屏幕尺寸。

小型 普通 大型 超大型

根据您的需求,您也可以有两个 APK,“小型及其他”或“超大型及其他”。图表着色还可以简化团队内部沟通——无论它涵盖多少不同的屏幕类型,您现在都可以简单地将每个 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-red

创建项目后,将库项目作为参考添加到每个 APK 项目。如果可能,在库项目中定义您的启动 Activity,并在您的 APK 项目中扩展该 Activity。在库项目中定义启动 Activity 使您有机会将所有应用程序初始化放在一个地方,这样每个单独的 APK 都无需重新实现“通用”任务,例如初始化分析、运行许可证检查以及任何其他不会从一个 APK 更改到另一个 APK 的初始化过程。

调整清单文件

当用户通过 Google Play 下载使用多个 APK 的应用程序时,将使用两条简单的规则选择要使用的正确 APK

  • 清单必须显示该特定 APK 合格
  • 在合格的 APK 中,版本号最高的获胜

例如,让我们以前面描述的多个 APK 集为例,并假设每个 APK 都已设置为支持大于其“目标”屏幕尺寸的所有屏幕尺寸。单独来看,每个 APK 的可能范围如下所示

小型 普通 大型 超大型
小型 普通 大型 超大型
小型 普通 大型 超大型

但是,通过使用“版本号最高的获胜”规则,如果我们将每个 APK 中的 versionCode 属性设置为 red ≥ green ≥ blue,则图表将有效地折叠为:

小型 普通 大型 超大型

现在,让我们进一步假设 Red APK 有一些其他两个 APK 不需要的要求。Android 开发者指南的Google Play 上的过滤器页面列出了所有可能的罪魁祸首。为了举例说明,让我们假设红色需要一个前置摄像头。事实上,Red APK 的全部意义在于利用额外的可用屏幕空间来使用前置摄像头进行娱乐。但是,事实证明,并非所有超大型设备都具有前置摄像头!太可怕了!

幸运的是,如果用户从这样的设备浏览 Google Play,Google Play 将查看清单,看到 Red 将前置摄像头列为要求,并将悄悄地忽略它,因为它已经确定 Red 和该设备并非天作之合。然后,它将看到 Green 不仅与超大型设备兼容,而且也不关心是否有前置摄像头!用户仍然可以从 Google Play 下载该应用,因为尽管发生了整个前置摄像头的问题,但仍然有一个 APK 支持该特定屏幕尺寸。

为了将所有 APK 保持在不同的“轨道”上,拥有良好的版本代码方案非常重要。推荐的方案可以在我们的开发者指南的版本代码部分找到。由于 APK 示例集只处理三个可能维度中的一个,因此将每个 APK 分开 1000 并从那里递增就足够了。这可能看起来像

蓝色:1001、1002、1003、1004……
绿色:2001、2002、2003、2004……
红色:3001、3002、3003、3004……

将所有这些放在一起,您的 Android 清单文件可能如下所示

蓝色

<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
  • 仔细检查清单过滤器中是否有冲突的信息(仅支持 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 正在针对目标设备。

有关在 Google Play 上发布多个 APK 的更多信息,请阅读多个 APK 支持