共计 4516 个字符,预计需要花费 12 分钟才能阅读完成。
HarmonyOS 2 提供了两种利用开发语言:Java 和 JS。Java 线程个性可能让多任务并行,充分利用硬件资源开发出高性能的利用。而 JS 却是一个单线程语言,无奈像 Java 一样创立新的 Thread,用 JS 语言开发是否会导致硬件资源无奈充分利用的状况呢?
本文给大家介绍“ACE JS 的单线程异步机制”就是解决这个问题的。然而,说到“单线程”与“异步”,大家可能会比拟纳闷,因为单线程和异步在概念上是抵触的,单线程无奈做到多任务并发,也就不会存在异步这种通信机制。
的确,JS 语言自身是无奈实现异步的,然而 ACE JS 框架却提供了多线程的宿主环境,通过音讯通信机制让 JS 语言有了异步的属性,上面咱们来详细描述其原理。
ACE 开发框架
应用 JS 开发 HarmonyOS 利用,应用的开发框架名为 ACE(Ability Cross-Platform Environment),该框架实用于手机、平板、智慧屏、智慧表、车机等设施,具备“一次开发,多端部署”的能力。
ACE 框架包含应用层(Application)、前端框架层(Framework)、引擎层(Engine)和平台适配层(Porting Layer),如下图所示:
●Application
应用层示意开发者应用 JS UI 框架开发的 FA 利用,这里的 FA 利用特指 JS FA 利用。
●Framework
前端框架层次要实现前端页面解析,以及提供 MVVM(Model-View-ViewModel)开发模式、页面路由机制和自定义组件等能力。
●Engine
引擎层次要提供动画解析、DOM(Document Object Model)树构建、布局计算、渲染命令构建与绘制、事件治理等能力。
●Porting Layer
适配层次要实现对平台层进行形象,提供形象接口,能够对接到零碎平台。比方:事件对接、渲染管线对接和零碎生命周期对接等。
ACE 开发框架的线程模型
每个 HarmonyOS JS 利用,都是通过上图所示的 ACE 开发框架进行加载渲染的。ACE 开发框架蕴含了 JS 线程、UI 线程、GPU 线程、IO 线程,并且在 ACE 框架外还会存在一类后台任务线程。
其中 GPU 线程与 IO 线程次要是 ACE 框架初始化与页面加载渲染的过程须要的,为 ACE 框架外部的专有线程,不会被利用间接操作到,利用不须要特地关注;UI 线程、JS 线程和后台任务线程会与利用开发代码相干,前面着重剖析这三个线程的作用和关系。
●UI 线程:负责利用界面的绘制刷新,与利用的过程号雷同,又叫主线程。如果开发 JS+JAVA 的混合编程,JAVA PA(Particle Ability)的 onStart/onConnect 等 Ability 生命周期回调便是运行在主线程,若在这些生命周期回调上执行耗时操作则会导致 JS UI 的绘制刷新卡住。
●JS 线程:利用的 JS 代码会被 JS 引擎解析执行,并运行在 JS 线程上,而 JS 又是单线程语言,所以目前咱们工程中看到的所有的 JS 代码都会执行在这个过程下惟一的 JS 线程上。
●后台任务线程:这里是对 ACE 框架内部的后盾线程的一个统称,并不单指一个线程,也并不惟一。后台任务线程蕴含了 Java PA 线程、文件操作 API、网络拜访 API 外部实现等相干线程。
上面咱们联合测试代码来看一下这 3 个线程之间的关系。
JS 线程与 UI 线程的关系
为了验证 JS 线程与 UI 线程的关系,咱们筹备了一个试验性质的 Demo,次要代码以及运行过程的 Log 如下:
首先咱们在 IDE 建设一个 Empty Ablity(JS) 模板的 HelloWorld 工程,在生命周期、按钮响应回调办法里减少 Log 以察看线程状况。刚创立的 app.js 中 Application 生命周期默认曾经有 Log,无需额定增加。
咱们只须要在主界面 index.js 文件中 onInit 减少日志:
console.info(‘page.default onInit’);
而后在 index.hml 中减少一个 button 以及会始终进行动画的 progress 组件:
<button id=’button1′ onclick=”onButtonClick”>I’m a button</button>
<progress type=”circular”/>
最初在 index.js 中减少按钮点击响应事件以及 Log,并且尝试 sleep 阻塞 js 线程:
function sleep(delay) {
for (var t = Date.now(); Date.now() – t <= delay;);
}
onButtonClick() {
console.info(‘onButtonClick begin’);
sleep(1000);
console.info(‘onButtonClick end’);
}
将利用运行起来,点击两次按钮,失去如下 Log:
从输入的 Log 中,咱们能够看到这个 JS FA 过程号为 22592,也就是说 UI 线程是 22592;生命周期回调以及按钮响应均在 24077 线程,这个就是 JS 线程,所以 JS 线程与 UI 线程不是同一个线程。
并且咱们尝试通过 sleep 办法阻塞 JS 线程,想察看 JS 线程阻塞是否会影响到 UI 线程的刷新。最终得出的论断是无论 JS 线程 sleep 多长时间,UI 界面上的 progress 组件动画始终会一直刷新,按钮也会有按压成果变动,所以咱们能够揣测 JS 线程与 UI 线程的互相调用应该是通过某种音讯机制实现的,而不是阻塞式的调用。
JS 线程与后台任务线程的关系
ACE JS 框架提供了 JS FA(Feature Ability)调用 Java PA(Particle Ability)的机制,该机制提供了一种通道来传递办法调用、解决数据返回以及订阅事件上报。咱们同样制作一个 Demo 来验证 JS 线程与 Java PA 线程的关系:
在 JS 中,咱们通过 FeatureAbility.callAbility 拉起并调用了名为一个类名为 ServiceAbility 的 Java PA,并拿到返回后果:
var action = {};
action.bundleName = ‘com.blancwu.test’;
action.abilityName = ‘com.blancwu.test.ServiceAbility’;
action.messageCode = 1001;
action.abilityType = 0;
action.syncOption = 0;
console.info(‘FeatureAbility.callAbility begin’ + JSON.stringify(action));FeatureAbility.callAbility(action).then(function (value) {
console.info(‘FeatureAbility.callAbility async result ‘ + JSON.stringify(value));
})
console.info(‘FeatureAbility.callAbility end’ + JSON.stringify(action));
在 ServiceAbility 的 onRemoteRequest 中减少 Log 输入,并 sleep 1 秒钟,以便察看线程状况与之间关系:
@Override
public boolean onRemoteRequest(int code, MessageParcel data, MessageParcel reply, MessageOption option) throws RemoteException {
HiLog.info(LABEL_LOG, “onRemoteRequest begin ” + code);
if (code == 1001) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Map<String, Object> result = new HashMap<String, Object>();
result.put(“result”, 1);
reply.writeString(ZSONObject.toZSONString(result));
}
HiLog.info(LABEL_LOG, “onRemoteRequest end ” + code);
return super.onRemoteRequest(code, data, reply, option);
}
以上代码实现后,咱们进行执行,失去的 Log 如下:
咱们察看到本次运行主过程(UI 线程)号为 4133,JS 代码执行在 JS 线程 5887,Java PA 响应 onRemoteRequest 执行在另一个后台任务线程 5837。通过 Log 咱们看到 onRemoteRequst 即便阻塞了后台任务线程 1s 也不会影响 JS 线程的并行执行以及主线程(UI 线程)上动画的刷新,做到了 JS 线程与后台任务线程异步地执行事务。
JS 线程的异步机制
下面从代码试验角度观察到了 JS 线程与其余线程的异步关系,那么 JS 线程是怎么解决来自其余多个线程的调用的呢?咱们先来看一下传统的浏览器环境下的机制:
上图中,JS 线程中的函数调用会存在于栈(stack)中,栈中的函数能够调用浏览器环境提供的 WebAPIs,蕴含了 DOM、ajax、timeout 等 API,这些 API 会在浏览器环境提供的另外一个内部线程执行,执行实现后会在工作队列(callback queue)中退出对应的回调事件(如 onClick、onLoad、onDone)。当栈中的代码执行结束,即栈清空后,JS 线程又会通过 event loop 取出工作队列中的下一个工作进行执行,以此类推实现整个的程序执行。更具体的机制能够去看阮一峰老师介绍 JS EventLoop 的文章:
● JS EventLoop 介绍
http://www.ruanyifeng.com/blo…
HarmonyOS ACE 开发框架同样遵循上述最根本的 EventLoop 调度机制,并且提供了更多的机制和 API,让业务逻辑能够在内部线程执行,蕴含了下面提到的 Java PA 以及异步回调的零碎能力 API。其中,异步回调的零碎能力 API 蕴含如文件系统操作和网络操作等,具体大家能够依照咱们试验 Demo 的办法去尝试一下。
● 参考
https://developer.harmonyos.c…
将来倒退的瞻望
目前 ACE JS 利用内实现多线程的最佳形式是通过混合编程调用 Java PA 形式,但将来纯 JS 利用肯定会越来越多,那么,只反对单线程的 JS ACE 框架的异步 API 能解决各种简单场景的问题吗?
单线程的 JS 加上异步 API 可能很好解决单个 I / O 阻塞的问题,然而如果遇到大量的 I / O 事件,比方批删除大量文件,通过 for 循环发动了大量异步工作,也会升高执行效率,甚至阻塞其余异步工作的执行。并且如果要应用 JS 语言开发计算密集型的工作,也无奈在惟一的 JS 线程上进行。
这时就须要一个真正的 JS 多线程解决机制了,尽管目前 HarmonyOS 2 还未反对,但将来 HarmonyOS 会思考布局出与 HTML5 相似提供反对 WebWorker 机制,反对开发出多线程的 JS 代码,提供给利用开发者更多的施展空间。