如果您将应用发布到 Google Play,则应构建并上传 Android 应用包。这样做时,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 架构(例如,对于 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 的清单文件中的元素指定。但是,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 版本)
要以运行 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 级别 的支持。例如,您可以发布您的应用程序,其中一个 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)提供单独的包。与其将所有可用库打包到一个 APK 中,您可以为每个 ABI 构建一个单独的 APK,并且只包含该 ABI 所需的库。要了解有关基于目标 ABI 生成单独的 APK 的更多信息,请访问构建多个 APK。
其他支持Google Play 过滤器—但未在上面列出—的清单元素将照常应用于每个 APK。但是,Google Play 不允许您根据这些设备特性的变化发布单独的 APK。因此,如果上面列出的过滤器对每个 APK 都相同(但 APK 基于清单或 APK 中的其他特性有所不同),则您无法发布多个 APK。例如,您无法提供不同的 APK,它们纯粹基于<uses-configuration>
特性有所不同。
多个 APK 的规则
在您为您的应用程序发布多个 APK 之前,您需要了解以下规则
- 您为同一个应用程序发布的所有 APK **必须具有相同的包名,并使用相同的证书密钥进行签名**。
- 每个 APK **必须具有不同的版本代码**,由
android:versionCode
属性指定。 - 每个 APK **不能完全匹配另一个 APK 的配置支持**。
也就是说,每个 APK 必须至少声明对支持的 Google Play 过滤器(上面列出)中的一个的不同支持。
通常情况下,您将根据特定的特性(例如支持的纹理压缩格式)区分您的 APK,因此每个 APK 将声明对不同设备的支持。但是,发布支持略有重叠的多个 APK 是可以的。当两个 APK 重叠时(它们支持一些相同的设备配置),落在重叠范围内的设备将接收版本代码更高的 APK(由
android:versionCode
定义)。 - 您无法激活版本代码低于要替换的 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 的版本代码必须更高。这确保了具有 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 的版本具有最低的版本代码。因此,如果设备同时支持 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 的版本代码。您还应该在代码中包含您的实际版本名称(即分配给 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 的配置支持时,设备如何接收更新。