Google 致力于为黑人社区推动种族平等。查看具体举措

针对不同的 API 级别创建多个 APK

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

若要开发 Android 应用以在 Google Play 上利用多个 APK,您需要从一开始就采取一些好的做法,以免在开发过程的后期遇到不必要的麻烦。这节课向您介绍如何创建应用的多个 APK,并使每个 APK 支持的 API 级别范围略有不同。此外,我们还会向您提供一些必要的工具,帮助您尽可能轻松地维护多 APK 代码库。

确认您需要多个 APK

当您尝试创建可在多代 Android 平台上运行的应用时,您自然希望应用能够利用新设备上的新功能,同时又不影响向后兼容性。乍一看,支持多 APK 似乎就是最佳的解决方案,但事实往往并非如此。多 APK 开发者指南的改用单个 APK 部分提供了一些有关如何使用单个 APK 实现该目标的有用信息,包括如何使用我们的支持库。您还可以学习如何在单个 APK 中编写仅在某些 API 级别运行的代码,而无需借助计算开销大的技术,如本文中的反射技术。

如果您可以将应用限制为单个 APK,则这种做法有以下几点优势:

  • 发布和测试更加轻松
  • 只需要维护一个代码库
  • 您的应用可以适应设备配置的变化
  • 可以跨设备执行应用恢复
  • 您不必担心市场偏好、从一个 APK“升级”到下一个 APK 的行为,或者哪个 APK 与哪类设备相匹配

本节课的后续内容假设您已经研究了这个主题,认真学习了链接资源中的资料,并确定创建多个 APK 是适合您应用的正确方法。

制作您的需求图表

首先,您可以创建一个简单的图表,快速确定您需要多少个 APK 以及每个 APK 所支持的 API 范围。为方便您参考,Android 开发者网站的平台版本页面提供了有关搭载给定版本 Android 平台的活跃设备数量的相关数据。此外,虽然跟踪每个 APK 要支持的 API 级别一开始听起来很简单,但很快就会变得非常困难,特别是在存在重叠时(通常都存在)。幸运的是,您可以快速轻松地制作出您的需求图表,以便于以后参考。

要创建多 APK 图表,请先绘制一行单元格,用于表示 Android 平台的不同 API 级别。在末尾添加一个额外的单元格,用于表示 Android 的未来版本。

3 4 5 6 7 8 9 10 11 12 13 +

现在只需在图表中着色,使每种颜色代表一个 APK。下面的示例说明了如何将每个 APK 对应到特定范围的 API 级别。

3 4 5 6 7 8 9 10 11 12 13 +

创建此图表后,将其分发给您的团队。针对您的项目的团队沟通马上变得简单了,因为您不用再问“API 级别 3 到 6 的 APK,就是 Android 1.x 1 怎么样了?进展如何?”此类复杂的问题,而只需要问“蓝色 APK 进展如何?”

将所有通用代码和资源放在一个库项目中

无论您是要修改现有的 Android 应用还是从头开始创建 Android 应用,这都是您应该执行的第一项也是最重要的一项代码库操作。放入库项目中的所有内容都只需要更新一次(例如经过本地化的字符串、颜色主题背景、共享代码中修复的问题),这样可以缩短开发时间并减少发生原本很容易避免的错误的可能性。

注意:尽管有关如何创建和包含库项目的实现详情不在本课的讨论范围内,但阅读创建 Android 库有助于您跟上学习进度。

如果要将现有应用转换为支持多 APK,请搜索代码库,找出在不同 APK 之间通用的所有已本地化的字符串文件、值列表、主题背景颜色、菜单图标和布局,并将它们全部放在库项目中。不会发生太大变化的代码也应该放在库项目中。您可能会发现自己需要扩展这些类,以便将一两个方法从一个 APK 添加到另一个 APK。

另一方面,如果您要从头开始创建应用,请尽可能先尝试在库项目中编写代码,然后在必要时才将其移到单个 APK 中。从长远来看,比起将 Blob 逐个添加到各个 APK 中,然后过几个月再查看能否在不造成任何问题的前提下将该 Blob 移到库中,这种做法更易于管理。

创建新的 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 就不必重新实现“通用”任务,比如初始化 Analytics、运行许可检查,以及任何其他在不同 APK 之间没有太大差异的初始化过程。

调整清单

当用户通过 Google Play 下载使用多个 APK 的应用时,系统会使用 2 个简单的规则来选择正确的 APK:

  • 清单必须显示特定的 APK 符合条件
  • 在符合条件的 APK 中,选择版本最高的一个

让我们以上文中的一组多个 APK 为例,假设我们尚未为任何 APK 设置最大 API 级别。具体来说,每个 APK 的可能范围如下:

3 4 5 6 7 8 9 10 11 12 13 +
3 4 5 6 7 8 9 10 11 12 13 +
3 4 5 6 7 8 9 10 11 12 13 +

我们知道具有较高 minSdkVersion 的 APK 也应该具有较高的版本代码,因此就 versionCode 值而言,红色 ≥ 绿色 ≥ 蓝色。因此,我们实际上可以将该图表简化为下表:

3 4 5 6 7 8 9 10 11 12 13 +

现在,让我们进一步假设红色 APK 有一些另外 2 种颜色的 APK 所没有的要求。Android 开发者指南中 Google Play 上的过滤器页面上列出了所有可能的问题根源。举例来说,假设红色 APK 需要前置摄像头。实际上,红色 APK 的全部意义就在于将前置摄像头与 API 11 中添加的出色新功能结合起来。但事实是,有些 API 11+ 设备根本没有前置摄像头!这太糟糕了!

幸运的是,如果用户在这样的设备上浏览 Google Play,Google Play 会查看清单,看到红色 APK 列出了前置摄像头的要求并直接忽略它,因为 Google Play 已确定红色 APK 和该设备不匹配。然后,Google Play 会发现绿色 APK 不仅可以向前兼容具有 API 11 的设备(因为未定义 maxSdkVersion),而且不在乎是否有前置摄像头!用户仍然可以从 Google Play 下载该应用,因为尽管发生了前置摄像头的小意外,但仍然有 APK 可以支持该特定 API 级别。

为了便于区分各个 APK,必须制定有效的版本代码方案。您可以在开发者指南中的版本代码部分找到推荐的方案。由于这组示例 APK 只涉及了 3 个可能维度中的一个,因此将每个 APK 隔开 1000,再由此进行递增就足够了。相应版本代码可能类似如下:

蓝色:03001, 03002, 03003, 03004...
绿色:07001, 07002, 07003, 07004...
红色:11001, 11002, 11003, 11004...

综上所述,您的 Android 清单内容可能类似于这样:

蓝色:

    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        android:versionCode="03001" android:versionName="1.0" package="com.example.foo">
        <uses-sdk android:minSdkVersion="3" />
        ...
    

绿色:

    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        android:versionCode="07001" android:versionName="1.0" package="com.example.foo">
        <uses-sdk android:minSdkVersion="7" />
        ...
    

红色:

    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        android:versionCode="11001" android:versionName="1.0" package="com.example.foo">
        <uses-sdk android:minSdkVersion="11" />
        ...
    

检查发布前核对清单

在上传到 Google Play 之前,请仔细检查以下各项内容。请注意,这些内容都是与多个 APK 相关的,并不能代表上传到 Google Play 的所有应用的完整核对清单。

  • 所有 APK 必须具有相同的软件包名称
  • 所有 APK 必须使用相同的证书签名
  • 如果 APK 在平台版本上出现重叠,则 minSdkVersion 较高的 APK 应具有更高的版本号
  • 仔细检查您的清单过滤器是否有冲突的信息(仅在特大屏幕上支持 Cupcake 的 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: 'small' 'normal' 'large' 'xlarge'
    supports-any-density: 'true'
    locales: '--_--'
    densities: '120' '160' '240'
    

检查 aapt 输出时,务必要检查 supports-screens 和 compatible-screens 的值是否存在冲突,以及您在清单中设置的权限是否导致添加了意外的“uses-feature”值。上例中的 APK 将对许多设备不可见。

为什么?添加必要的 SEND_SMS 权限时,就隐式添加了 android.hardware.telephony 功能要求。由于 API 11 是 Honeycomb(专门针对平板电脑进行优化的 Android 版本),并且 Honeycomb 设备均没有电话硬件,因此 Google Play 会在这些情况下过滤掉此 APK,直到未来出现 API 级别更高且拥有电话硬件的设备。

幸运的是,通过将以下内容添加到清单可轻松解决此问题:

    <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 定位到正确的设备。恭喜您大功告成了!