我们经常收到关于 NativeScript Android 应用性能和 APK 大小的反馈,特别是使用 Angular 2 构建的应用。在这篇文章中,我将详细介绍 Android 运行时、核心模块和 Angular 2 集成项目背后的技术挑战,并分享一些可以显著提高性能的简单步骤。
在 NativeScript Android 应用加载时会执行几个任务。
注意: 文件提取仅在全新安装后首次运行应用程序时进行。在后续运行时,文件将直接从文件系统中读取。
前两个步骤的执行时间是固定的,没有优化的必要。但是,步骤 3 和 4 是导致加载时间增加的主要原因。如何优化这些步骤?请了解 android-snapshot 插件。
此 snapshot 插件优化了:
唯一的权衡是,由于快照文件依赖于 CPU,应用程序包大小会增加 5 MB。不过,我们有计划进一步改进这一点(有关更多信息,请参阅“包大小”部分)。
如果你查看 snapshot 存储库的 Readme 文件,你会看到我们测试的近似数字。启用快照后,空白 Anular 2 应用程序的加载时间几乎缩短了一倍 - 从 4 秒缩短到 2 秒!
V8 运行自己的垃圾收集器,Android(Dalvik VM)也是如此。由于 NativeScript 的架构范式,特别是通过 JavaScript 进行的 100% 本机访问,有时会在 JavaScript 中代理本机 Android 对象。因此,Android 运行时使用复杂的机制来使代理的本机实例保持活动状态,直到 JavaScript 代理对象被收集为止。问题是,两个垃圾收集器存在于不同的世界中,并且以自己的速度运行。Android 运行时尝试在 JavaScript 端分配大型本机对象(如 Image)时施加相同的内存压力,但这只是一个提示。有时,Dalvik GC 需要清除一些大型对象(如 Bitmaps)来释放堆内存,但由于这些对象在 JavaScript 中被代理,并且代理仍然处于活动状态,因此什么也不会发生,Dalvik 堆不会被清除。大多数情况下,JS 代理到那时可以被收集,但 V8 的 GC 尚未开始,而 Dalvik 的 GC 已经开始运行。最终会导致内存泄漏和 OutOfMemory Android 异常,尤其是在使用大型本机对象(如 Bitmaps 或流)时。有关更详细的技术细节的良好阅读材料和示例 可以在这里找到。
综上所述 - 有时我们需要一种机制来同步两个垃圾收集器,以确保正确回收内存。V8 在一个标志后面公开了一个 `gc()` 调用,该标志默认情况下在 NativeScript Android 应用程序中启用。NativeScript 核心模块利用此行为,在导航时调用 V8 的 `gc`。为什么在导航时调用?因为通常在导航操作之后,上一页面的 JavaScript 可视化树变得可以被 V8 的垃圾收集器访问。正如我已经提到的,收集代理将使相应的本机对象可以被 Dalvik 的垃圾收集器访问。
所有这些听起来像是完美的解决方案,但不幸的是,它会导致一些性能问题,因为 V8 的 GC 无条件地强制执行,因为它是在主 UI 线程上运行的阻塞操作。我们试图仅在 主应用程序循环空闲 时调用 V8 的 gc,但似乎这种启发式方法在导航期间没有按预期工作。
在导航期间显式调用 JavaScript GC 是 NativeScript 核心模块中主要的性能瓶颈之一。如果你今天遇到了这个问题,你可以 简单地注释掉 这行代码,看看效果如何。我们目前正在研究一个更通用的解决方案,该解决方案将在 Android 运行时中直接处理。虽然它也使用了一些启发式方法,但它看起来很有希望,并且似乎在迄今为止隔离的 99% 的内存泄漏场景中都能正常工作。
正如我在 这篇博文 中解释的那样,NativeScript Android 运行时提供三个独立的版本,用于如今可用的三种主要 CPU 架构。这使得一个空白的 Hello World 应用程序的大小约为 12 MB。如果 APK 大小对于您的客户至关重要,那么您可以为应用程序需要运行的每种 CPU 架构生成单独的 APK。有关更多信息,您可以参考 ABI 分割部分,该部分位于 适用于 Android 的发布帮助文章 中。
以下是您可以采取的措施来提高 NativeScript Android 应用程序的性能
我们计划进一步改进 NativeScript Android 应用程序整体性能的上述三个主要方面。
虽然 NativeScript 应用程序是 100% 本地的,并且在大多数情况下运行速度快且流畅,但有时应用程序可能需要额外的微调才能变得更快。NativeScript 工程团队正在积极努力,争取使所有上述改进默认情况下启用。一如既往,我们欢迎并感谢您的反馈 - 请在 GitHub NativeScript 和 Android 运行时 存储库中分享您的反馈。