返回博客首页
← 所有文章

使用 NativeScript 将任务卸载到工作线程

2019 年 4 月 18 日 — 作者:Rob Lauer

性能。性能。性能。

当我们与 NativeScript 开发者交流时,性能始终是他们选择 NativeScript 的首要原因之一。这也是我们永远无法获得足够多的东西。虽然移动硬件不断改进,但始终有方法来改进我们创建的应用程序的性能,进而改进用户体验。

除了在 iOS 和 Android 上提供真正的原生 UI 之外,NativeScript 还有一些额外的技巧可以让你为独特的场景定制你的应用程序,并从那些宝贵的 CPU 周期中榨取更多性能。

让我向你介绍 NativeScript 上的工作线程。

工作线程

在 Web 世界中,工作线程更广为人知的是 Web Worker(或 Service Worker),它允许你将 NativeScript 提供的单线程环境转变为多线程环境。

注意:NativeScript 中的 Workers API 松散地基于Web Workers APIWeb Workers 规范

Service Worker(一种 Web Worker)在渐进式 Web 应用 (PWA) 开发人员中非常流行,因为它允许进行通知和其他非阻塞任务。但 PWA 功能结束的地方,NativeScript 就开始发挥作用了。

正如这个 NativeScript 演示应用程序所示,99% 的时间单线程模型都很好(因为在 UI 线程上运行所有内容都非常快)

nativescript demo app

但是,在某些情况下,操作可能需要更长时间,从而影响应用程序的 UI。这可能导致应用程序感觉比预期慢。

为了解决这个问题,NativeScript 提供了一个Workers API,它允许你创建在单独的线程上执行的任务,这些线程与主应用程序上下文完全隔离。

让我们看一些何时(以及何时不)在 NativeScript 应用程序中使用工作线程的示例。

何时使用工作线程

理论上,几乎任何可以在同步 UI 操作之外运行的任务都是工作线程的候选对象。

提示:在开始认为工作线程是解决复杂性能问题的灵丹妙药之前,请确保考虑启动额外线程所需的开销。

CPU 密集型、与 UI 无关的任务可能是工作线程最能发挥作用的最佳示例。如果你非常频繁地使用 Google Analytics,你可能会发现自己在应用程序中测量每个用户操作、页面浏览量、功能使用情况和远程服务调用。即使这些调用应该异步运行,它们仍然会对主线程产生负面影响。

另一个很好的例子是图像处理——本身就是一项 CPU 密集型任务,当你将其混合到 UI 线程时会变得更加复杂!

这些用于NativeScript CoreAngular的演示应用程序提供了一个卸载图像处理的极佳示例。

示例代码

让我们看看如何自己构建一个非常简单的 Worker。

var worker = new Worker("./workers/my-worker.js");

以上代码启动一个新的线程(运行时实例)并执行引用的脚本(my-worker.js)。接下来,我们将希望与 Worker 通信和/或接收来自它的消息或结果。这是通过消息实现的。

// send a message to our worker
worker.postMessage("Hello worker thread!");

// receive a message from our worker
worker.onmessage = function(msg) {
    console.log("Received this message from the worker thread: " + msg.data);
}

在我们的my-worker.js文件中,我们将提供onmessage方法来接收来自主线程的消息。

onmessage = function(msg) {
    console.log("Received this message from the main thread: " + msg.data);

    // perform some crazy cpu-intensive task here!

    // send a message back to the main thread
    postMessage("Hello main thread!");

    close();
}

错误处理和终止线程

使用工作线程可能有点令人害怕,因为保持打开状态的线程会消耗资源,并可能导致内存使用量膨胀并破坏性能。因此,请确保捕获任何错误并在完成时关闭/终止线程。

my-worker.js中的基本示例

onerror = function(e) {
    console.log("Oh no! Worker thread error: " + e);
    return true;
}

在你的应用程序代码中(如果你想显示错误)

worker.onerror = function(e) {
    console.log("Worker thread error: " + e);
}

重要:工作线程会一直保持活动状态,直到被主线程显式终止或自身关闭!

使用close();(如上所示)在my-worker.js中关闭线程。或者使用worker.terminate();在你的应用程序代码中终止线程。

如果工作线程未被终止/关闭,则垃圾回收器将不会回收和处理 Worker 实例。

何时不使用工作线程

务必记住,每次启动一个新的工作线程时,都会增加应用程序的资源和内存占用。这意味着,如果你一次启动过多线程或在错误的情况下使用它们,你的应用程序的性能实际上可能会下降

如果你认为工作线程可以帮助你处理来自表单的输入、显示图表或许多其他基本应用程序功能,请再考虑一下。NativeScript 框架已经针对这些场景中的绝大多数进行了优化。

在开发过程中,你最好的选择始终是在各种 iOS 和 Android 物理设备上测量应用程序的功能性能!

也就是说,在主线程性能不足的极端情况下,Worker 非常有用。你所能做的最好的事情就是测试各个场景并衡量哪种方法最适合你的情况。