HarmonyOS JS應用開發需要關注哪些執行緒?官方解析來啦~

科技前鋒發表於2021-09-06

作者: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),如下圖所示:

HarmonyOS JS應用開發需要關注哪些執行緒?官方解析來啦

  • Application

應用層表示開發者使用JS UI框架開發的FA應用,這裡的FA應用特指JS FA應用。https://developer.harmonyos.com/cn/docs/documentation/doc-guides/ui-js-fa-developing-0000001063300612

  • 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.com/cn/docs/documentation/doc-references/js-apis-fa-calls-pa-mechanism-0000001050022401),需要特別注意的是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' 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執行緒。

HarmonyOS 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}"


整個執行過程可以描述如下圖:

HarmonyOS JS應用開發需要關注哪些執行緒?官方解析來啦

我們觀察到本次執行主程式(UI執行緒)號為4133,JS程式碼執行在JS執行緒(5887),Java PA響應onRemoteRequest執行在另一個後臺任務執行緒(5837)。透過Log我們看到onRemoteRequst即使阻塞了後臺任務執行緒1s也不會影響JS執行緒的執行以及主執行緒(UI執行緒)上動畫的重新整理,做到了JS執行緒與後臺任務執行緒上的任務並行處理。

五、JS的非同步機制

上面從程式碼實驗角度觀察到了應用開發中JS執行緒與其他執行緒的關係,那麼JS執行緒是怎麼與其他執行緒進行非同步通訊的呢?我們先來看一下傳統的瀏覽器環境下的機制:

HarmonyOS 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/blog/2014/10/event-loop.html

HarmonyOS的JS UI框架同樣遵循上述最基本的EventLoop排程機制,並且提供了更多的機制和API,讓業務邏輯可以在外部執行緒執行;包括上面提到的Java PA機制以及還未提及的支援非同步回撥的系統能力API。其中,支援非同步回撥的系統能力API包含檔案系統操作和網路操作等,感興趣的同學可以按照我們實驗Demo類似的方法去嘗試一下。

● 檔案系統操作API參考

https://developer.harmonyos.com/cn/docs/documentation/doc-references/js-apis-file-storage-0000000000629445

六、未來展望

目前HarmonyOS的JS應用內實現多執行緒的最佳方式是透過混合程式設計呼叫Java PA方式,但無法支援純JS應用開發多執行緒業務,純JS應用目前僅可使用框架提供的非同步API了,那麼這些非同步API能解決各種複雜場景的問題嗎?

JS執行緒加上非同步API能夠很好解決單個I/O阻塞的問題,但是如果遇到大量的I/O事件,比如批刪除大量檔案,透過for迴圈發起了大量非同步任務,也會降低執行效率,甚至阻塞其他非同步任務的執行。並且如果要使用JS語言開發計算密集型的任務,也無法在唯一的JS執行緒上進行。

這時就需要一個真正的JS多執行緒處理機制了,雖然目前HarmonyOS 2還未支援,但未來HarmonyOS會考慮規劃出與HTML5類似提供支援WebWorker機制,支援開發出多執行緒的JS程式碼,提供給應用開發者更多的發揮空間。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69997629/viewspace-2790570/,如需轉載,請註明出處,否則將追究法律責任。

相關文章