构建特定于 ABI 的 APK

本文讨论如何构建一个使用 Xamarin.Android 以单个 ABI 为目标的 APK。

概述

在某些情况下,应用程序拥有多个 APK 可能更有利 - 每个 APK 都使用相同的密钥存储进行签名,并共享相同的软件包名称,但它是针对特定设备或 Android 配置进行编译的。 这不是推荐的方法 - 拥有一个可以支持多种设备和配置的 APK 是非常简单的。 在某些情况下,创建多个 APK 可能很有用,例如:

  • 减小 APK 的大小 - Google Play 对 APK 文件强加了 100MB 大小的限制。 创建特定于设备的 APK 可以减小 APK 的大小,因为你只需为应用程序提供一部分资产和资源。

  • 支持不同的 CPU 体系结构 - 如果应用程序具有特定 CPU 的共享库,则只能分发该 CPU 的共享库

多个 APK 可能会使分发变得复杂 - 这是 Google Play 解决的问题。 Google Play 将确保根据应用程序的版本代码和 AndroidManifest.XML 中包含的其他元数据将正确的 APK 传递到设备。 有关 Google Play 如何支持应用程序多个 APK 的具体详细信息和限制,请查阅关于多个 APK 支持的 Google 文档

本指南介绍如何为 Xamarin.Android 应用程序构建多个 APK 的脚本,每个 APK 都针对一个特定的 ABI。 它包含以下主题:

  1. 为 APK 创建一个唯一的版本代码
  2. 创建一个将用于此 APK 的 AndroidManifest.XML 的临时版本。
  3. 使用上一步中的 AndroidManifest.XML 构建应用程序。
  4. 通过对 APK 进行签名并使用 zipalign 为其优化来准备发布。

本指南最后将演示如何使用 Rake 编写这些步骤的脚本。

为 APK 创建版本代码

Google 为使用七位数版本代码的版本代码推荐了一种特定算法(请参阅多 APK 支持文档中的“使用版本代码方案”一节)。 通过将此版本代码方案扩展为八位数字,可以将一些 ABI 信息包含在版本代码中,以确保 Google Play 将正确的 APK 分发到设备。 以下列表解释了这个八位数版本代码格式(从左到右编制索引):

  • 索引 0(下图中的红色)- ABI 的整数:

    • 1 – armeabi
    • 2 – armeabi-v7a
    • 6 – x86
  • 索引 1-2(下图中的橙色)- 应用程序支持的最低 API 级别。

  • 索引 3-4(下图中的蓝色)- 支持的屏幕大小:

    • 1 - 小
    • 2 - 常规
    • 3 - 大
    • 4 - 加大
  • 索引 5-7(下图中的绿色)- 版本代码的唯一编号。 这由开发人员设置。 它应为应用程序的每个公开发布版本增加。

下图说明了上面列表中描述的每个代码的位置:

八位数版本代码格式的图示,用颜色编码

Google Play 确保根据 versionCode 和 APK 配置将正确的 APK 发送到设备。 将具有最高版本代码的 APK 发送到设备。 例如,应用程序可能有三个 APK,其版本代码如下:

  • 11413456 - ABI 是 armeabi;针对 API 级别 14;小屏幕到大屏幕;版本号为 456。
  • 21423456 - ABI 是 armeabi-v7a;针对 API 级别 14;常规屏幕和大屏幕;版本号为 456。
  • 61423456 - ABI 是 x86;针对 API 级别 14;常规屏幕和大屏幕;版本号为 456。

要继续使用此示例,假设已修复一个特定于 armeabi-v7a 的错误。 应用版本增加到 457,并且将 android:versionCode 设置为 21423457 来构建新的 APK。 armeabix86 版本的版本代码将保持不变。

现在假设 x86 版本接收一些针对更新 API(API 级别 19)的更新或错误修复,使该应用版本成为 500。 当 armeabi/armeabi-v7a 保持不变时,新的 versionCode 将更改为 61923500。 此时,版本代码将是:

  • 11413456 - ABI 是 armeabi;针对 API 级别 14;小屏幕到大屏幕;版本名称为 456。
  • 21423457 - ABI 是 armeabi-v7a;针对 API 级别 14;常规屏幕和大屏幕;版本名称为 457。
  • 61923500 - ABI 是 x86;针对 API 级别 19;常规屏幕和大屏幕;版本名称为 500。

手动维护这些版本代码可能会对开发人员带来沉重的负担。 计算正确的 android:versionCode 然后构建 APK 的过程应该是自动执行的。 本文末尾的演练将举例介绍如何执行此操作。

创建临时的 AndroidManifest.XML

虽然不是绝对必要,但为每个 ABI 创建临时的 AndroidManifest.XML 可以帮助防止由于在 APK 之间泄露信息可能出现的问题。 例如,android:versionCode 属性对于每个 APK 都是唯一的,这一点至关重要。

完成此任务的方式取决于所涉及的脚本系统,但通常涉及获取开发过程中使用的 Android 清单副本,修改副本,然后在构建过程中使用修改后的清单。

编译 APK

如以下示例命令行中所示,通过使用 xbuildmsbuild 能最好地完成为每个 ABI 构建 APK 的任务:

/Library/Frameworks/Mono.framework/Commands/xbuild /t:Package /p:AndroidSupportedAbis=<TARGET_ABI> /p:IntermediateOutputPath=obj.<TARGET_ABI>/ /p:AndroidManifest=<PATH_TO_ANDROIDMANIFEST.XML> /p:OutputPath=bin.<TARGET_ABI> /p:Configuration=Release <CSPROJ FILE>

下表解释了每个命令行参数:

  • /t:Package - 创建使用调试密钥存储进行签名的 Android APK

  • /p:AndroidSupportedAbis=<TARGET_ABI> - 这是要面向的 ABI。 必须为 armeabiarmeabi-v7ax86 之一。

  • /p:IntermediateOutputPath=obj.<TARGET_ABI>/ - 这是保存作为版本一部分创建的中间文件的目录。 如果需要,Xamarin.Android 将创建一个以 ABI 命名的目录,例如 obj.armeabi-v7a。 建议为每个 ABI 使用一个文件夹,因为这样可以防止在版本之间“泄露”文件导致的问题。 请注意,此值以目录分隔符(在 OS X 的情况下为 /)结束。

  • /p:AndroidManifest - 此属性指定将在构建期间使用的 AndroidManifest.XML 文件的路径。

  • /p:OutputPath=bin.<TARGET_ABI> - 这是容纳最终 APK 的目录。 Xamarin.Android 将创建一个以 ABI 命名的目录,例如 bin.armeabi-v7a

  • /p:Configuration=Release - 执行 APK 的发布版本。 调试版本可能无法上传到 Google Play。

  • <CS_PROJ FILE> - 这是 Xamarin.Android 项目的 .csproj 文件的路径。

为 APK 签名并使用 Zipalign 为其优化

在通过 Google Play 发布 APK 之前,必须先为其签名。 这可以通过使用属于 Java 开发人员工具包的 jarsigner 应用程序来执行。 下面的命令行演示如何在命令行中使用 jarsigner

jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore <PATH/TO/KEYSTORE> -storepass <PASSWORD> -signedjar <PATH/FOR/SIGNED_JAR> <PATH/FOR/JAR/TO/SIGN> <NAME_OF_KEY_IN_KEYSTORE>

必须先使用 Zipalign 为所有的 Xamarin.Android 应用程序优化,然后才能在设备上运行。 这是要使用的命令行格式:

zipalign -f -v 4 <SIGNED_APK_TO_ZIPALIGN> <PATH/TO/ZIP_ALIGNED.APK>

通过 Rake 自动创建 APK

示例项目 OneABIPerAPK 是一个简单的 Android 项目,演示如何计算 ABI 特定的版本号并为以下所有的 ABI 构建三个独立的 APK:

  • armeabi
  • armeabi-v7a
  • x86

示例项目中的 rakefile 执行前几节中描述的所有步骤:

  1. 为 APK 创建 android:versionCode

  2. android:versionCode 写入该 APK 的自定义 AndroidManifest.XML

  3. 为 Xamarin.Android 项目编译发布版本,该项目特别面向 ABI 并使用在上一步中创建的 AndroidManifest.XML

  4. 使用生产密钥存储为 APK 签名

  5. 使用 Zipalign 优化 APK。

要构建应用程序的所有 APK,请从命令行运行 build Rake 任务:

$ rake build
==> Building an APK for ABI armeabi with ./Properties/AndroidManifest.xml.armeabi, android:versionCode = 10814120.
==> Building an APK for ABI x86 with ./Properties/AndroidManifest.xml.x86, android:versionCode = 60814120.
==> Building an APK for ABI armeabi-v7a with ./Properties/AndroidManifest.xml.armeabi-v7a, android:versionCode = 20814120.

rake 任务完成后,将有三个包含文件 xamarin.helloworld.apkbin 文件夹。 下一个屏幕截图显示了每个文件夹及其内容:

包含 xamarin.helloworld.apk 的平台特定文件夹的位置

注意

本指南中概述的构建过程可以在许多不同的构建系统之一中实现。 尽管我们没有预先编写的示例,但 Powershell / psakeFake 应该也适用。

总结

本指南提供了一些关于如何创建针对指定 ABI 的 Android APK 的建议。 此外,还讨论了创建 android:versionCodes 的一种可能的方案,该方案将识别该 APK 所针对的 CPU 体系结构。 演练包括一个使用 Rake 编写脚本的示例项目。