多 APK 支持

如果您将应用发布到 Google Play,您应该构建并上传一个 Android App Bundle。当您这样做时,Google Play 会自动为每个用户的设备配置生成并提供优化的 APK,这样他们只需下载运行应用所需的代码和资源。如果您不发布到 Google Play,那么发布多个 APK 会很有用,但您必须自行构建、签名和管理每个 APK。

多 APK 支持是 Google Play 上的一项功能,它允许您为应用发布面向不同设备配置的 APK。每个 APK 都是您应用的完整独立版本,但它们在 Google Play 上共享相同的应用详情页面,并且必须共享相同的软件包名称并使用相同的发布密钥进行签名。此功能对于您的应用无法通过单个 APK 覆盖所有所需设备的情况非常有用。

Android 设备可能在多个方面有所不同,因此让您的应用尽可能多地支持各种设备对于应用的成功至关重要。Android 应用通常通过为不同配置提供替代资源(例如,不同屏幕尺寸的不同布局)并在运行时由 Android 系统选择适合设备的资源,从而在大多数兼容设备上以单个 APK 运行。然而,在少数情况下,单个 APK 无法支持所有设备配置,因为替代资源会使 APK 文件过大,或者其他技术挑战导致单个 APK 无法在所有设备上运行。

为了帮助您将应用发布到尽可能多的设备上,Google Play 允许您在相同的应用详情页面下发布多个 APK。然后,Google Play 会根据您在每个 APK 的清单文件中声明的配置支持,将相应的 APK 提供给适当的设备。

通过发布包含多个 APK 的应用,您可以:

  • 每个 APK 支持不同的 OpenGL 纹理压缩格式。
  • 每个 APK 支持不同的屏幕尺寸和密度。
  • 每个 APK 支持不同的设备功能集。
  • 每个 APK 支持不同的平台版本。
  • 每个 APK 支持不同的 CPU 架构(例如,当您的应用使用 Android NDK 时,支持 ARM 或 x86)。
  • 针对运行 Android (Go edition) 的入门级设备进行优化。

目前,这些是 Google Play 支持的唯一可用于将多个 APK 发布为同一应用的设备特性。

注意:要了解如何在 Google Play 上准备和发布 APK,请参阅准备和发布版本支持文章。

多 APK 的工作原理

在 Google Play 上使用多个 APK 的理念是,您的应用在 Google Play 中只有一个条目,但不同的设备可能会下载不同的 APK。这意味着:

  • 您只需维护一套产品详情(应用说明、图标、屏幕截图等)。这也意味着您不能为不同的 APK 设定不同的价格。
  • 所有用户在 Google Play 上都只会看到您的一个应用版本,因此他们不会因为您可能已发布的“平板电脑专用”或“手机专用”等不同版本而感到困惑。
  • 所有用户评价都将应用于相同的应用详情页面,即使不同设备上的用户可能拥有不同的 APK。
  • 如果您为不同版本的 Android(针对不同 API 级别)发布了不同的 APK,那么当用户的设备收到符合您已发布的不同 APK 条件的系统更新时,Google Play 会将用户的应用更新为针对更高 Android 版本设计的 APK。与应用关联的任何系统数据都会保留(与使用单个 APK 进行常规应用更新时相同)。

支持的过滤器

哪些设备收到哪个 APK 取决于每个 APK 的清单文件中元素指定的 Google Play 过滤器。但是,Google Play 仅在每个 APK 使用过滤器支持以下设备特性变体时才允许您发布多个 APK:

  • OpenGL 纹理压缩格式

    这基于您的清单文件中的 <supports-gl-texture> 元素。

    例如,在开发使用 OpenGL ES 的游戏时,您可以为支持 ATI 纹理压缩的设备提供一个 APK,并为支持 PowerVR 压缩的设备提供一个单独的 APK(还有许多其他压缩格式)。

  • 屏幕尺寸(以及可选的屏幕密度)

    这基于您的清单文件中的 <supports-screens> <compatible-screens> 元素。您不应同时使用这两个元素,并且应尽可能只使用 <supports-screens>

    例如,您可以提供一个支持小尺寸和正常尺寸屏幕的 APK,以及另一个支持大尺寸和超大尺寸屏幕的 APK。要详细了解如何根据屏幕尺寸或密度生成单独的 APK,请参阅构建多个 APK

    请考虑以下支持所有屏幕尺寸的最佳实践:

    • Android 系统为应用提供了强大的支持,使其可以通过单个 APK 支持所有屏幕配置。除非绝对必要,否则您应避免创建多个 APK 来支持不同的屏幕,而应遵循支持多种屏幕指南,以便您的应用具有灵活性,并可以通过单个 APK 适应所有屏幕配置。
    • 默认情况下,如果未另行声明, <supports-screens> 元素中的所有屏幕尺寸属性都为“true”。但是,由于 android:xlargeScreens 属性是在 Android 2.3 (API 级别 9) 中添加的,如果您的应用未将 android:minSdkVersion android:targetSdkVersion 设置为“9”或更高,Google Play 将假定其为“false”。
    • 您不应在清单文件中同时使用 <supports-screens> <compatible-screens> 元素。同时使用这两个元素会增加因它们之间的冲突而引入错误的可能性。如需帮助决定使用哪个,请阅读分发到特定屏幕。如果您无法避免同时使用,请注意,对于给定尺寸之间的任何协议冲突,“false”将优先。

  • 设备功能集

    这基于您的清单文件中的 <uses-feature> 元素。

    例如,您可以为支持多点触控的设备提供一个 APK,并为不支持多点触控的设备提供另一个 APK。如需了解平台支持的功能列表,请参阅功能参考

  • Android (Go edition)

    要以运行 Android (Go edition) 的设备为目标,您的 APK 需要声明 <uses-feature android:name="android.hardware.ram.low" android:required="true">,目标 API 级别至少为 26,并且版本代码要高于您的非 Go edition APK。

  • API 级别

    这基于您的清单文件中的 <uses-sdk> 元素。您可以使用 android:minSdkVersionandroid:maxSdkVersion 属性来指定对不同 API 级别的支持。

    例如,您可以发布一个 APK,支持 API 级别 16 - 19 (Android 4.1.x - 4.4.4) — 仅使用 API 级别 16 或更低版本提供的 API — 以及另一个 APK,支持 API 级别 21 及更高版本 (Android 5.0+) — 使用 API 级别 21 或更低版本提供的 API。要了解如何构建分别针对不同 API 范围的 APK,请参阅配置产品变体

    如果您将此特性用作区分多个 APK 的因素,则 android:minSdkVersion 值更高的 APK 必须具有更高的 android:versionCode 值。如果两个 APK 基于不同的受支持过滤器重叠其设备支持,此规则也适用。这可确保当设备收到系统更新时,Google Play 可以向用户提供您的应用的更新(因为更新基于应用版本代码的增加)。有关此要求的更多说明,请参阅下面关于多 APK 规则的部分。

    通常应避免使用 android:maxSdkVersion,因为只要您使用公共 API 正确开发了应用,它就始终与未来的 Android 版本兼容。如果您想为更高的 API 级别发布不同的 APK,您仍然不需要指定最高版本,因为如果一个 APK 中的 android:minSdkVersion"16",而另一个是 "21",则支持 API 级别 21 或更高版本的设备将始终收到第二个 APK(因为其版本代码更高,如前述说明所述)。


  • CPU 架构 (ABI)

    一些原生库为特定的 CPU 架构或应用二进制接口 (ABI) 提供单独的软件包。您可以为每个 ABI 构建单独的 APK,并且只包含该 ABI 所需的库,而不是将所有可用库打包到一个 APK 中。要详细了解如何根据目标 ABI 生成单独的 APK,请参阅构建多个 APK

启用 Google Play 过滤器的其他清单元素(但上面未列出)仍照常应用于每个 APK。但是,Google Play 不允许您根据这些设备特性的变体发布单独的 APK。因此,如果上述列出的过滤器对于每个 APK 都相同(但 APK 基于清单或 APK 中的其他特性而不同),则您无法发布多个 APK。例如,您不能提供纯粹基于 <uses-configuration> 特性而不同的 APK。

多 APK 规则

在为您的应用发布多个 APK 之前,您需要了解以下规则:

  • 您为同一应用发布的所有 APK 必须具有相同的软件包名称并使用相同的证书密钥进行签名
  • 每个 APK 必须具有不同的版本代码,由 android:versionCode 属性指定。
  • 每个 APK 不得与另一个 APK 的配置支持完全匹配

    也就是说,每个 APK 必须对至少一个受支持的 Google Play 过滤器(如上所列)声明略有不同的支持。

    通常,您会根据特定特性(例如受支持的纹理压缩格式)来区分您的 APK,因此,每个 APK 将声明对不同设备的支持。但是,发布支持范围略有重叠的多个 APK 也是可以的。当两个 APK 重叠时(它们支持某些相同的设备配置),落在重叠范围内的设备将收到具有更高版本代码的 APK(由 android:versionCode 定义)。

  • 您不能激活一个版本代码低于其所替换 APK 的新 APK。例如,假设您有一个版本代码为 0400 的活动 APK,支持小尺寸到正常尺寸的屏幕,然后您尝试用一个版本代码为 0300 的 APK 替换它,该 APK 支持相同的屏幕尺寸。这将引发错误,因为这意味着旧 APK 的用户将无法更新应用。
  • 需要更高 API 级别的 APK 必须具有更高的版本代码

    仅在以下任一情况下此规则成立:APK 仅根据受支持的 API 级别不同(没有其他受支持的过滤器可以区分 APK)或者 APK 确实使用了另一个受支持的过滤器,但该过滤器中的 APK 之间存在重叠。

    这很重要,因为用户的设备只有在 Google Play 上的 APK 版本代码高于设备上当前 APK 的版本代码时,才会从 Google Play 收到应用更新。这可确保如果设备收到系统更新,使其有资格安装更高 API 级别的 APK,则设备会收到应用更新,因为版本代码会增加。

    注意:版本代码增加的大小无关紧要;它只需在支持更高 API 级别的版本中更大即可。

    以下是一些示例:

    • 如果您上传的针对 API 级别 16 及更高版本 (Android 4.1.x+) 的 APK 的版本代码为 0400,则针对 API 级别 21 及更高版本 (Android 5.0+) 的 APK 的版本代码必须为 0401 或更大。在这种情况下,API 级别是唯一使用的受支持过滤器,因此版本代码必须与每个 APK 的 API 级别支持相关联地增加,以便用户在收到系统更新时获得更新。
    • 如果您有一个针对 API 级别 16(及更高版本)小到大尺寸屏幕的 APK,以及另一个针对 API 级别 21(及更高版本)大到超大尺寸屏幕的 APK,则版本代码必须与 API 级别相关联地增加。在这种情况下,API 级别过滤器用于区分每个 APK,屏幕尺寸也是如此。由于屏幕尺寸重叠(两个 APK 都支持大尺寸屏幕),版本代码仍必须按顺序排列。这可确保收到 API 级别 21 系统更新的大尺寸屏幕设备将收到第二个 APK 的更新。
    • 如果您有一个针对 API 级别 16(及更高版本)小到正常尺寸屏幕的 APK,以及另一个针对 API 级别 21(及更高版本)大到超大尺寸屏幕的 APK,则版本代码无需与 API 级别相关联地增加。由于屏幕尺寸过滤器内没有重叠,因此没有设备可能在这两个 APK 之间移动,因此无需从较低的 API 级别到较高的 API 级别增加版本代码。
    • 如果您有一个针对 API 级别 16(及更高版本) ARMv7 CPU 的 APK,以及另一个针对 API 级别 21(及更高版本) ARMv5TE CPU 的 APK,则版本代码必须与 API 级别相关联地增加。在这种情况下,API 级别过滤器用于区分每个 APK,CPU 架构也是如此。由于包含 ARMv5TE 库的 APK 与具有 ARMv7 CPU 的设备兼容,因此这些 APK 在此特性上重叠。因此,支持 API 级别 21 及更高版本的 APK 的版本代码必须更高。这可确保具有 ARMv7 CPU 且收到 API 级别 21 系统更新的设备将收到为 API 级别 21 设计的第二个 APK 的更新。但是,由于此类更新会导致 ARMv7 设备使用未针对该设备 CPU 完全优化的 APK,因此您应该在每个 API 级别为 ARMv5TE 和 ARMv7 架构都提供一个 APK,以优化每个 CPU 上的应用性能。注意:在比较包含 ARMv5TE 和 ARMv7 库的 APK 时适用,而在比较其他原生库时不适用。

未能遵守上述规则将在您激活 APK 时在 Google Play 管理中心引发错误 — 您将无法发布您的应用,直到您解决该错误。

在您激活 APK 时可能会出现其他冲突,但它们将导致警告而非错误。警告可能由以下原因引起:

  • 当您修改某个 APK 以“缩小”对设备特性的支持,并且没有其他 APK 支持那些超出支持范围的设备时。例如,如果某个 APK 当前支持小尺寸和正常尺寸屏幕,而您将其更改为仅支持小尺寸屏幕,则您已缩小了受支持设备的范围,并且某些设备将不再在 Google Play 上看到您的应用。您可以通过添加另一个支持正常尺寸屏幕的 APK 来解决此问题,以便所有先前受支持的设备仍然受到支持。
  • 当两个或更多 APK 之间存在“重叠”时。例如,如果一个 APK 支持小尺寸、正常尺寸和大尺寸屏幕,而另一个 APK 支持大尺寸和超大尺寸屏幕,则存在重叠,因为两个 APK 都支持大尺寸屏幕。如果您不解决此问题,则符合两个 APK 条件的设备(示例中的大尺寸屏幕设备)将收到版本代码最高的 APK。

    注意:如果您正在为不同的 CPU 架构创建单独的 APK,请注意,用于 ARMv5TE 的 APK 将与用于 ARMv7 的 APK 重叠。也就是说,为 ARMv5TE 设计的 APK 与 ARMv7 设备兼容,但反之则不然(仅包含 ARMv7 库的 APK 与 ARMv5TE 设备兼容)。

当出现此类冲突时,您将看到警告消息,但您仍然可以发布您的应用。

创建多个 APK

一旦您决定发布多个 APK,您可能需要为每个要发布的 APK 创建单独的 Android 项目,以便您可以适当地单独开发它们。您可以通过简单地复制现有项目并为其指定新名称来做到这一点。(或者,您可以使用构建系统,根据构建配置输出不同的资源,例如纹理。)

提示:避免重复大部分应用代码的一种方法是使用库项目。库项目包含共享代码和资源,您可以将其包含在实际的应用项目中。

为同一个应用创建多个项目时,最好为每个项目指定一个名称,表明将对 APK 施加的设备限制,以便您轻松识别它们。例如,“HelloWorld_21”可能是为 API 级别 21 及更高版本设计的应用的不错名称。

注意:您为同一应用发布的所有 APK 必须具有相同的软件包名称并使用相同的证书密钥进行签名。请务必了解多 APK 规则中的每条规则。

分配版本代码

同一应用的每个 APK 必须具有唯一的版本代码,由 android:versionCode 属性指定。发布多个 APK 时,您必须谨慎分配版本代码,因为它们必须各不相同,但在某些情况下,必须或应根据每个 APK 支持的配置以特定顺序定义。

版本代码排序

需要更高 API 级别的 APK 通常必须具有更高的版本代码。例如,如果您创建两个 APK 来支持不同的 API 级别,则用于更高 API 级别的 APK 必须具有更高的版本代码。这可确保如果设备收到系统更新,使其有资格安装用于更高 API 级别的 APK,则用户会收到更新应用的通知。有关此要求如何适用的更多信息,请参阅上面关于多 APK 规则的部分。

您还应该考虑版本代码的顺序如何影响用户收到的 APK,无论是由于不同 APK 的覆盖范围重叠,还是您将来可能对 APK 进行的更改。

例如,如果您根据屏幕尺寸拥有不同的 APK,例如一个用于小尺寸到正常尺寸,另一个用于大尺寸到超大尺寸,但预见到将来您会将 APK 更改为一个用于小尺寸,一个用于正常尺寸到超大尺寸,那么您应该将大尺寸到超大尺寸 APK 的版本代码设得更高。这样,当您进行更改时,正常尺寸设备将收到适当的更新,因为版本代码从现有 APK 增加到现在支持该设备的新 APK。

此外,在创建基于对不同 OpenGL 纹理压缩格式支持而不同的多个 APK 时,请注意许多设备支持多种格式。由于当两个 APK 的覆盖范围重叠时,设备会收到版本代码最高的 APK,因此您应该对 APK 之间的版本代码进行排序,以使具有首选压缩格式的 APK 具有最高的版本代码。例如,您可能希望使用 PVRTC、ATITC 和 ETC1 压缩格式为您的应用执行单独的构建。如果您严格按照此顺序偏好这些格式,则使用 PVRTC 的 APK 应具有最高的版本代码,使用 ATITC 的 APK 具有较低的版本代码,而使用 ETC1 的版本具有最低的版本代码。因此,如果设备同时支持 PVRTC 和 ETC1,它将收到带有 PVRTC 的 APK,因为它具有最高的版本代码。

如果 Google Play 商店无法为目标设备识别要安装的正确 APK,您可能还希望创建一个通用 APK,其中包含您想要支持的所有不同设备变体的资源。如果您确实提供了通用 APK,则应为其分配最低的 versionCode。因为 Google Play 商店会安装与目标设备兼容且具有最高 versionCode 的应用版本,所以为通用 APK 分配较低的 versionCode 可确保 Google Play 商店在回退到更大的通用 APK 之前,会尝试安装您的其他 APK 之一。

使用版本代码方案

为了允许不同的 APK 独立于其他 APK 更新其版本代码(例如,当您只修复一个 APK 中的错误时,无需更新所有 APK),您应该为您的版本代码使用一个方案,该方案在每个 APK 之间提供足够的空间,以便您可以在一个 APK 中增加代码而无需增加其他 APK。您还应该在代码中包含您的实际版本名称(即分配给 android:versionName 的用户可见版本),以便您轻松关联版本代码和版本名称。

注意:当您增加 APK 的版本代码时,Google Play 将提示旧版本的用户更新应用。因此,为避免不必要的更新,您不应增加实际未包含更改的 APK 的版本代码。

我们建议使用至少 7 位数字的版本代码:表示受支持配置的整数位于高位,版本名称(来自 android:versionName)位于低位。例如,当应用版本名称为 3.1.0 时,API 级别 4 APK 和 API 级别 11 APK 的版本代码将分别为 0400310 和 1100310。前两位数字保留给 API 级别(分别为 4 和 11),中间两位数字用于屏幕尺寸或 GL 纹理格式(这些示例中未使用),最后三位数字用于应用的版本名称 (3.1.0)。图 1 显示了两个基于平台版本 (API 级别) 和屏幕尺寸拆分的示例。

图 1. 建议的版本代码方案,其中前两位数字用于 API 级别,第二和第三位数字用于最小和最大屏幕尺寸(1 - 4 分别表示四种尺寸)或表示纹理格式,最后三位数字用于应用版本。

此版本代码方案只是一个建议,说明您应该如何建立一个随着应用演进而可扩展的模式。特别是,此方案并未展示用于识别不同纹理压缩格式的解决方案。一个选项是定义您自己的表格,为您的应用支持的每种不同压缩格式指定不同的整数(例如,1 可能对应 ETC1,2 对应 ATITC 等)。

您可以使用任何您想要的方案,但您应该仔细考虑您的应用未来版本如何需要增加其版本代码,以及当设备配置更改(例如,由于系统更新)或当您修改一个或多个 APK 的配置支持时,设备如何接收更新。