为不同的屏幕尺寸创建多个 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 应用程序还是从头开始创建新的应用程序,这都是您应该对代码库执行的第一件事,并且也是最重要的事情。库项目中包含的所有内容只需要更新一次(例如语言本地化字符串、颜色主题、共享代码中修复的 bug),这可以提高您的开发效率并降低出现本可以轻松避免的错误的可能性。

注意:虽然有关如何创建和包含库项目的实现细节超出了本课程的范围,但您可以通过阅读创建 Android 库来快速入门。

如果您要将现有应用程序转换为使用多 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 需要一个前置摄像头。事实上,Red APK 的全部意义在于利用额外的可用屏幕空间来使用前置摄像头进行娱乐。但是,事实证明,并非所有超大屏幕设备都有前置摄像头!太可怕了!

幸运的是,如果用户从一台这样的设备上浏览 Google Play,Google Play 会查看清单,看到 Red 将前置摄像头列为一项要求,并将其静默忽略,因为它已确定 Red 和该设备不是天作之合。然后,它会看到 Green 不仅与超大屏幕设备兼容,而且也不在乎是否有前置摄像头!用户仍然可以从 Google Play 下载该应用程序,因为尽管发生了前置摄像头故障,但仍然有一个 APK 支持该特定屏幕尺寸。

为了将所有 APK 保持在单独的“轨道”上,拥有良好的版本代码方案非常重要。推荐的方案可以在我们的开发者指南的版本代码部分找到。由于 APK 示例集仅处理 3 个可能维度之一,因此将每个 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,并且在同一个清单中同时使用这两个标签通常是一个非常糟糕的主意。它使事情变得不必要地复杂,并增加了出错的可能性。另请注意,清单明确设置了每个屏幕尺寸的值,而不是利用默认值(small 和 normal 默认情况下始终为 true)。这可以帮助您避免以后出现问题。例如,目标 SDK 为 < 9 的清单会将 xlarge 自动设置为 false,因为该尺寸当时还不存在。所以要明确!

检查您的预发布清单

在上传到 Google Play 之前,请仔细检查以下项目。请记住,这些项目与多个 APK 特别相关,绝不代表上传到 Google Play 的所有应用程序的完整清单。

  • 所有 APK 必须具有相同的包名
  • 所有 APK 必须使用相同的证书签名
  • 您希望 APK 支持的每个屏幕尺寸,都应在清单中设置为 true。您希望它避免的每个屏幕尺寸都应设置为 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 正在针对目标设备。

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