关于harmonyos:HarmonyOS-JS应用开发需要关注哪些线程官方解析来啦

30次阅读

共计 7150 个字符,预计需要花费 18 分钟才能阅读完成。

作者:wuyawei,华为软件开发工程师

HarmonyOS 2 提供了对两种开发语言的反对:Java 和 JavaScript(下文简称 JS)。从事过 Android 开发的同学对 Java 都很相熟了,其多线程个性可能让多任务并行执行,充分利用硬件资源开发出高性能的利用。在 HarmonyOS 2 上,JS 目前无奈像 Java 一样间接创立新的 Thread,那么应用 JS 语言开发 HarmonyOS 利用是否会遇到硬件资源无奈充分利用的状况呢?

尽管应用 JS 语言目前无奈间接创立新的 Thread,然而 HarmonyOS 的 JS UI 框架提供了多线程的宿主环境,能够帮忙利用开发丰盛的业务逻辑。在开发 HarmonyOS 2 利用时,开发者除了须要理解 JS 线程外,还须要关注哪些线程?这些线程之间的关系又是什么样的?上面让咱们一起来钻研一下。

一、HarmonyOS 的 JS UI 框架

HarmonyOS 的 JS UI 框架包含应用层(Application)、前端框架层(Framework)、引擎层(Engine)和平台适配层(Porting Layer),如下图所示:

• Application

应用层示意开发者应用 JS UI 框架开发的 FA 利用,这里的 FA 利用特指 JS FA 利用。https://developer.harmonyos.c…

• Framework

前端框架层次要实现前端页面解析,以及提供 MVVM(Model-View-ViewModel)开发模式、页面路由机制和自定义组件等能力。

• Engine

引擎层次要提供动画解析、DOM(Document Object Model)树构建、布局计算、渲染命令构建与绘制、事件治理等能力。

• Porting Layer

适配层次要实现对平台层进行形象,提供形象接口,能够对接到零碎平台。比方:事件对接、渲染管线对接和零碎生命周期对接等。

二、JS UI 框架的线程模型

每个 HarmonyOS JS 利用,都是通过 JS UI 框架进行加载渲染的。HarmonyOS 的 JS UI 框架蕴含了 JS 线程、UI 线程、GPU 线程、IO 线程这 4 个线程,并且在 JS UI 框架外还会存在一类后台任务线程。

其中 GPU 线程与 IO 线程次要是 JS UI 框架初始化与页面加载渲染的过程须要的,为 JS UI 框架外部的专有线程,不会被利用间接操作到,利用不须要特地关注;UI 线程、JS 线程和后台任务线程会与利用开发代码相干,前面着重剖析这三个线程的作用和关系。

UI 线程 :负责利用界面的绘制刷新,与利用的过程号雷同,又叫主线程。如果开发 JS+JAVA 的混合编程(https://developer.harmonyos.c…),须要特地留神的是 JAVA PA(Particle Ability)的 onStart/onConnect 等 Ability 生命周期回调便是运行在主线程,若在这些申明周期回调上执行耗时操作则会导致 JS UI 的绘制刷新卡住。
JS 线程:利用的 JS 代码会被 JS 引擎解析执行,并运行在 JS 线程上,目前咱们工程中看到的所有的 JS 代码都会执行在这个过程下惟一的 JS 线程上。
后台任务线程 :这是对 JS UI 框架内部的后盾线程的一个统称,并不单指一个线程,也并不惟一。它蕴含了 Java PA 中 onRemoteRequest() 业务逻辑的执行线程、文件操作 API、网络拜访 API 外部实现等相干线程。

上面咱们联合测试代码的来看一下这 3 个利用开发须要关注的线程之间的关系。

三、JS 线程与 UI 线程的关系

为了验证 JS 线程与 UI 线程的关系,咱们筹备了一个试验性质的 Demo,次要代码以及运行过程的 Log 如下:

首先咱们在 IDE 建设一个 Empty Ability(JS)模板的 HelloWorld 工程,在生命周期、按钮响应回调办法里减少 Log 以察看线程状况。刚创立的 app.js 中 Application 生命周期默认曾经有 Log,无需额定增加。

咱们须要在主动创立的 MainAbility.java 中 onStart/onTouchEvent 回调函数减少 HiLog 打印:

HiLog.info(LABEL_LOG, “MainAbility::onStart”);

HiLog.info(LABEL_LOG, “MainAbility::onTouchEvent”);

咱们只须要在主界面 index.js 文件中 onInit 减少日志:

CoffeeScript

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’);

}

咱们将利用运行起来,点击一次按钮,会发现 progress 组件动画并不会因为 onButtonClick 阻塞了 1 秒钟而有任何暂停,咱们一起来剖析一下这个过程中的 Log:

15:30:07.323 15870-15870/com.blancwu.test I 01100/MainAbility: MainAbility::onStart

15:30:07.342 15870-18938/com.blancwu.test I 03B00/JSApp: app Log: AceApplication onCreate

15:30:07.352 15870-18938/com.blancwu.test I 03B00/JSApp: app Log: page.default onInit

15:30:31.006 15870-15870/com.blancwu.test I 01100/MainAbility: MainAbility::onTouchEvent

15:30:31.041 15870-15870/com.blancwu.test I 01100/MainAbility: MainAbility::onTouchEvent

15:30:31.104 15870-15870/com.blancwu.test I 01100/MainAbility: MainAbility::onTouchEvent

15:30:31.106 15870-15870/com.blancwu.test I 01100/MainAbility: MainAbility::onTouchEvent

15:30:31.112 15870-18938/com.blancwu.test I 03B00/JSApp: app Log: onButtonClick begin

15:30:32.113 15870-18938/com.blancwu.test I 03B00/JSApp: app Log: onButtonClick end

从输入的 Log 中,工夫点前面跟着的便是咱们执行日志的代码行所在的过程号与线程号,方才咱们减少的日志均在 15870 这个过程下,这个过程下又存在 15870 线程以及 18938 线程。其中 15870 与过程号雷同,这便是咱们说的 UI 线程;咱们在.js 文件中减少的日志全都会在 18938 线程上打印进去,这个便是 JS 线程。

在利用初始化时,首先进入 MainAbility.java 的 onStart 生命周期回调,而后才进入 AceApplication、Page 等 JS 代码逻辑;利用初始化结束后,UI 线程上便会继续刷新 progress 组件的动画,当用户点击按钮触发 onButtonClick 阻塞 1 秒时,因为阻塞的仅仅是 JS 线程,所以 UI 线程上 progress 组件的动画刷新并不会有任何影响,还是在继续刷新。所以咱们能够确定 JS 线程与 UI 线程的互相调用应该是通过某种音讯机制实现的,而不是阻塞式的调用。

四、JS 线程与后台任务线程的关系

JS UI 框架提供了 JS FA(Feature Ability)调用 Java PA(Particle Ability)的机制,该机制提供了一种通道来传递办法调用、解决数据返回以及订阅事件上报的机制,Java PA 运行在一个独立的后台任务线程,能够撑持利用开发多线程的业务逻辑。咱们同样制作一个 Demo 来验证 JS 线程与 Java PA 线程的关系:

在上一个 Demo 根底上,咱们批改 onButtonClick 的 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);

}

以上代码实现后,咱们进行执行,progress 组件的动画同样不会被打断,失去的 Log 如下:

06-25 13:31:48.090 4133-5887/com.blancwu.test I 03B00/JSApp: app Log: FeatureAbility.callAbility begin{“bundleName”:”com.blancwu.test”,”abilityName”:”com.blancwu.test.ServiceAbility”,”messageCode”:1001,”abilityType”:0,”syncOption”:0}

06-25 13:31:48.094 4133-5887/com.blancwu.test I 03B00/JSApp: app Log: FeatureAbility.callAbility end{“bundleName”:”com.blancwu.test”,”abilityName”:”com.blancwu.test.ServiceAbility”,”messageCode”:1001,”abilityType”:0,”syncOption”:0}

06-25 13:31:48.112 4133-4133/com.blancwu.test E 01100/ServiceAbility: [8187916a4418bed, 399b373, f521b3] ServiceAbility::onStart

06-25 13:31:48.126 4133-5837/com.blancwu.test I 01100/ServiceAbility: [8187916a4418bed, 171378f, 385abb1] onRemoteRequest begin 1079135572

06-25 13:31:48.126 4133-5837/com.blancwu.test I 01100/ServiceAbility: [8187916a4418bed, 171378f, 385abb1] onRemoteRequest end 1079135572

06-25 13:31:48.126 4133-5837/com.blancwu.test I 00000/RemoteObject: [8187916a4418bed, 171378f, 385abb1] Java onRemoteRequest called

06-25 13:31:48.143 4133-5837/com.blancwu.test I 01100/ServiceAbility: onRemoteRequest begin 1001

06-25 13:31:49.145 4133-5837/com.blancwu.test I 01100/ServiceAbility: onRemoteRequest end 1001

06-25 13:31:49.145 4133-5837/com.blancwu.test I 00000/RemoteObject: Java onRemoteRequest called

06-25 13:31:49.151 4133-5887/com.blancwu.test I 03B00/JSApp: app Log: FeatureAbility.callAbility async result “{\”result\”:1}”

整个执行过程能够形容如下图:

咱们察看到本次运行主过程(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 的 JS UI 框架同样遵循上述最根本的 EventLoop 调度机制,并且提供了更多的机制和 API,让业务逻辑能够在内部线程执行;包含下面提到的 Java PA 机制以及还未提及的反对异步回调的零碎能力 API。其中,反对异步回调的零碎能力 API 蕴含文件系统操作和网络操作等,感兴趣的同学能够依照咱们试验 Demo 相似的办法去尝试一下。

文件系统操作 API 参考

https://developer.harmonyos.c…

六、将来瞻望

目前 HarmonyOS 的 JS 利用内实现多线程的最佳形式是通过混合编程调用 Java PA 形式,但无奈反对纯 JS 利用开发多线程业务,纯 JS 利用目前仅可应用框架提供的异步 API 了,那么这些异步 API 能解决各种简单场景的问题吗?

JS 线程加上异步 API 可能很好解决单个 I / O 阻塞的问题,然而如果遇到大量的 I / O 事件,比方批删除大量文件,通过 for 循环发动了大量异步工作,也会升高执行效率,甚至阻塞其余异步工作的执行。并且如果要应用 JS 语言开发计算密集型的工作,也无奈在惟一的 JS 线程上进行。

这时就须要一个真正的 JS 多线程解决机制了,尽管目前 HarmonyOS 2 还未反对,但将来 HarmonyOS 会思考布局出与 HTML5 相似提供反对 WebWorker 机制,反对开发出多线程的 JS 代码,提供给利用开发者更多的施展空间。

正文完
 0