如果您将应用发布到 Google Play,则应构建并上传 Android 应用包。这样做时,Google Play 会自动生成并提供针对每个用户的设备配置的优化 APK,因此他们只需下载运行您的应用所需的代码和资源。如果您没有发布到 Google Play,则发布多个 APK 很有用,但您必须自行构建、签名和管理每个 APK。
多 APK 支持是 Google Play 上的一项功能,允许您发布针对不同设备配置的不同 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 支持不同的平台版本。
- 支持不同的 CPU 架构(例如,对于 ARM 或 x86,当您的应用使用 Android NDK 时)。
- 针对运行 Android (Go 版本) 等入门级设备进行优化。
目前,这些是 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 是由 Google Play 筛选器 确定的,这些筛选器由每个 APK 清单文件中的元素指定。但是,只有当每个 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: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 版本)
要面向运行 Android (Go 版本) 的设备,您的 APK 需要声明
<uses-feature android:name="android.hardware.ram.low" android:required="true">
,至少面向 API 级别 26,并且版本号高于您的非 Go 版本 APK。 - API 级别
这基于您的清单文件的
<uses-sdk>
元素。您可以同时使用android:minSdkVersion
和android:maxSdkVersion
属性来指定对不同 API 级别支持。例如,您可以发布一个支持 API 级别 16-19(Android 4.1.x-4.4.4)的应用(仅使用 API 级别 16 或更低版本提供的 API)和另一个支持 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"
,另一个 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,其版本代码为
0400
,然后尝试将其替换为针对相同屏幕尺寸的版本代码为0300
的 APK。这会引发错误,因为它意味着先前 APK 的用户将无法更新应用。 - 需要**更高 API 级别**的 APK 必须具有**更高的版本代码**。
只有在以下任一情况下才为真:APK 仅基于支持的 API 级别有所不同(没有其他 支持的过滤器 将 APK 区分开来)或 APK 使用其他支持的过滤器,但在该过滤器中 APK 之间存在重叠。
这一点很重要,因为用户的设备只有当 Google Play 上 APK 的版本代码高于设备上 APK 的版本代码时,才会收到应用更新。这可确保如果设备收到系统更新,使其随后有资格安装针对更高 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 时,而不适用于比较其他原生库时。
- 如果您已上传的针对 API 级别 16 及更高版本(Android 4.1.x+)的 APK 的版本代码为
如果不遵守上述规则,在激活 APK 时 Google Play Console 会出现错误——在解决错误之前,您将无法发布您的应用程序。
激活 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 的 APK 具有最低的版本代码。因此,如果设备同时支持 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 可能对应于 ETC1,2 对应于 ATITC,依此类推)。
您可以使用任何您想要的方案,但您应该仔细考虑应用程序的未来版本如何需要增加其版本代码,以及当设备配置发生更改(例如,由于系统更新)或当您修改一个或多个 APK 的配置支持时,设备如何接收更新。