欢迎参加我们将于 6 月 3 日举行的 #Android11:Beta 版发布会

多 APK 支持

如果您将应用发布到 Google Play,您应构建并上传 Android App Bundle 文件。执行此操作后,Google Play 会自动为每个用户的设备配置生成并提供经过优化的 APK,以便他们仅下载运行应用所需的代码和资源。如果您不发布到 Google Play,则发布多个 APK 非常有用,但是您必须自己编译、签署和管理每个 APK。

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

不同的 Android 设备可能在某些方面存在差异,因此您应尽可能使应用支持尽可能多的设备,这对应用取得成功非常重要。Android 应用通常可通过单个 APK 在大多数兼容设备上运行,方法是为不同的配置提供备用资源(例如,针对不同屏幕尺寸提供不同布局),且 Android 系统会在运行时为设备选择合适的资源。但在少数情况下,单个 APK 无法支持所有设备配置,因为备用资源会导致 APK 文件过大,或者会有其他技术挑战阻止单个 APK 在所有设备上运行。

为了帮助您发布的应用支持尽可能多的设备,Google Play 允许您在同一个商店条目下发布多个 APK。然后,Google Play 会根据您在每个 APK 的清单文件中声明的配置支持,将每个 APK 提供给相应的设备。

使用多个 APK 发布应用,您可以:

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

目前,对于将多个 APK 作为同一应用发布而言,Google Play 仅支持这些设备特性。

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

多 APK 的工作原理

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

  • 您只保留一组商品详情(应用说明、图标、屏幕截图等)。这还意味着您不能为不同的 APK 收取不同的价格。
  • 所有用户在 Google Play 上只能看到您的应用的一个版本,因此他们不会因为您发布的“适用于平板电脑”或“适用于手机”的不同版本而感到迷惑。
  • 即使不同设备上的用户可能拥有不同的 APK,所有用户评价也会应用到同一个应用详情中。
  • 如果您针对不同版本的 Android 发布了不同版本的 APK(不同的 API 级别),那么当用户的设备接收到系统更新,使其有资格获得您已发布的其他 APK 时,Google Play 会将该用户应用更新为针对更高版本 Android 设计的 APK。系统会保留与应用相关联的所有系统数据(与使用单个 APK 时的常规应用更新相同)。

支持的过滤器

接收每个 APK 的设备由各 APK 清单文件中的元素所指定的 Google Play 过滤器决定。但是,只有当每个 APK 都使用过滤器来支持以下设备特性的变体时,Google Play 才允许您发布多个 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 2.3(API 级别 9)中添加了 android:xlargeScreens 属性,如果您的应用未将 android:minSdkVersionandroid:targetSdkVersion 设置为“9”或更高值,则 Google Play 会假定它为“false”。
    • 您不应在清单文件中同时使用 <supports-screens><compatible-screens> 两个元素。同时使用这两个元素会增加由于它们之间的冲突而发生错误的几率。要获取帮助以决定使用哪一个元素,请参阅分发到特定屏幕。如果您无法避免同时使用这两个元素,则请注意,对于指定尺寸在一致性方面的冲突,Google Play 会以“false”为准。
  • 设备功能集

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

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

  • Android(Go 版本)

    要以搭载 Android(Go 版本)的设备作为目标,您的 APK 需要声明 <uses-feature android:name="android.hardware.ram.low" android:required="true">,至少定位 API 级别 26,且版本号高于非 Go 版 APK。

  • API 级别

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

    例如,您可以使用以下两个 APK 来发布应用:一个 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,则仍然无需指定最高版本,因为如果 android:minSdkVersion 在一个 APK 中为 "16",而在另一个 APK 中为 "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,您无法激活该新 APK。例如,假设您有一个已启用的 APK,用于较小至普通的屏幕尺寸,版本号为 0400,接着您尝试将其替换为用于相同屏幕尺寸的 APK,版本号为 0300。这会引发错误,因为这意味着使用前一个 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 版本号必须更高。这样可确保接收到系统更新(更新至 API 级别 21)的具有 ARMv7 CPU 的设备会收到设计用于 API 级别 21 的第二个 APK 的更新。但是,由于此类更新导致 ARMv7 设备使用的 APK 未针对该设备的 CPU 进行全面优化,因此您应在每个 API 级别为 ARMv5TE 和 ARMv7 架构提供 APK,以便优化每个 CPU 上的应用性能。注意:这仅适用于将 APK 与 ARMv5TE 和 ARMv7 库进行比较的情况,而不适用于比较其他原生库。

不遵守上述规则会导致激活 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 设置的设备限制,以便轻松识别它们。例如,对于为 API 级别 21 及更高级别设计的应用而言,可以用“HelloWorld_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 的 APK 版本号最低。因此,如果设备同时支持 PVRTC 和 ETC1,则它会收到使用 PVRTC 的 APK,因为它具有最高的版本号。

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

使用版本号方案

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

注意:当您增加 APK 的版本号时,Google Play 会提示先前版本的用户更新应用。因此,为了避免不必要的更新,如果 APK 实际不包含更改,则您不应增加该 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 的配置支持时,设备将如何收到更新。