返回博客主页
← 所有文章

NativeScript Android 应用包大小揭秘

2016年5月25日 — 作者:Georgi Atanasov

关于 NativeScript 应用包大小的问题时不时会冒出来,特别是针对 Android 平台。这篇文章的目的是对这个问题进行深入透彻的解答,并解释为什么默认情况下,Android 应用包(*.APK)比一个空白的纯原生 Android 应用要额外增加约 12 MB。

首先,让我从 NativeScript Android 运行时的架构概述开始。

NativeScript Android 运行时

NativeScript Android 运行时包含几个主要部分,它们提供了核心功能。这些包括

  • 嵌入式 Google JavaScript 虚拟机 - V8 - 用于运行 JavaScript。
  • 一个 C++ 层,它告诉 V8 如何处理所有 Android API。
  • 一个 Java 层,它在原生 C++/JavaScript 与 Android API 之间起到粘合作用(反之亦然)。

前两个层是原生库(编译成机器码),它们依赖于 CPU 架构,也就是 ABI(应用程序二进制接口),不像 Java 和 JavaScript 那样是动态(JIT)编译的。

Android 如何处理原生库

Android 的难点在于它如何解析特定于 ABI 的原生二进制库。换句话说,它如何知道哪些二进制文件适用于 32 位设备,哪些适用于 64 位架构?以下解释直接摘自 Android 文档中的这篇 [优秀文章](https://android-docs.cn/ndk/guides/abis.html)

"

Android 系统在运行时知道它支持哪些 ABI,因为特定于构建的系统属性指示

  • 设备的主要 ABI,对应于系统映像本身使用的机器码。
  • 一个可选的辅助 ABI,对应于系统映像也支持的另一个 ABI。

这种机制确保系统在安装时从包中提取最佳机器码。安装应用程序时,包管理器服务会扫描 APK,并查找以下格式的共享库

lib/<primary-abi>/lib<name>.so

如果未找到,并且您定义了辅助 ABI,则服务会扫描以下格式的共享库

lib/<secondary-abi>/lib<name>.so

当它找到它正在寻找的库时,包管理器会将它们复制到 /lib/lib<name>.so,位于应用程序的数据目录下 (data/data/<package_name>/lib/)。"

NativeScript 中的 ABI 支持

为了让 NativeScript Android 应用程序在具有不同二进制接口的设备上可用,NativeScript Android 运行时包必须为其原生库提供正确的 ABI 版本。

如今最常见的 ABI 是

  • x86
  • armeabi-v7a
  • arm64-v8a

如果您创建了一个 NativeScript 项目并添加了 Android 平台(“tns platform add android”),请仔细查看此文件夹

<project-name>/platforms/android/libs/jni/

 

里面有三个文件夹,它们的名字与上面列出的 ABI 相匹配。这些文件夹确保您的应用程序几乎可以在所有现代 Android 设备上运行。但是,归档后,它们总共占用了 **约 10 MB**。

换句话说,默认情况下,Android 的空白 NativeScript 项目约为 12 MB,因为它们包含 **为不同的 Android CPU 架构构建的三个 NativeScript 运行时副本**。(由于 iOS 只有一个 CPU 架构,因此这对 iOS 上的 NativeScript 不是问题。)

故事结束了吗?还没有。

单个、自包含的 APK 不是支持正确 ABI 解析的唯一解决方案。Google Play 商店还支持一项名为“多个 APK”的功能。

多个 APK

这是一个 [Google Play 上的功能](https://android-docs.cn/google/play/publishing/multiple-apks.html),

“允许您为您的应用程序发布不同的 APK,每个 APK 针对不同的设备配置。每个 APK 是您应用程序的完整独立版本,但它们在 Google Play 上共享相同的应用程序列表。”

Google 鼓励开发者尽可能 **避免** 使用多个 APK,并推荐使用单个、自包含的应用程序包。但是,这在某些情况下要么是不可能的,要么是不需要的。例如,如果应用程序超过了 Google Play 的 APK 大小限制,即 100 MB,那么它显然需要拆分成多个 APK。或者,如果应用程序的目标受众预计带宽较低,那么开发者会希望通过各种方式减小 APK 大小。

NativeScript 中的多个 APK

在 NativeScript 应用程序中使用多个 APK 功能可以将默认的 12 MB APK 大小减少三倍 - 每个 ABI(x86、arm64-v8a、armeabi-v7a)最多减少约 4.5 MB。这提供了一种解决方法,可以在应用程序包大小很重要的情況下大幅减小 NativeScript APK 的大小。

不幸的是,NativeScript CLI 中还没有内置功能来自动化此过程。我们已经 [将此功能纳入计划](https://github.com/NativeScript/nativescript-cli/issues/1392),并将很快开始实现它。在那之前,如果 12 MB 的 APK 大小对您的应用程序来说是一个阻碍,您可以手动从 <project-name>/platforms/android/libs/jni/ 文件夹中删除不需要的 NativeScript 运行时 ABI 构建。

需要注意的是:如果您的应用程序在支持“APK 中不可用 ABI”的设备上启动,那么应用程序将直接崩溃。

总结

NativeScript CLI 将提供根据 ABI 生成多个应用程序包的可选功能。Google 建议尽可能使用单个 APK,我们支持这一建议,因此,对多个 APK 的支持仅适用于应用程序包大小至关重要的应用程序。虽然按 ABI 的方法可以节省大约 8 MB,但它会带来更复杂的发布过程,每个单独生成的包都会增加出错的可能性。但是,如果您愿意做出这种权衡,并且节省 8 MB 有助于您采用 NativeScript,那么目前,多个 APK 就是答案。