JS UI框架下FA與PA是如何交互的
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)
上一篇“HarmonyOS UI框架關(guān)鍵技術(shù)解析”中,給大家介紹了基于JS/JAVA UI框架開發(fā)簡單業(yè)務(wù)的方法。那么對于復(fù)雜業(yè)務(wù),在當(dāng)前HarmonyOS版本下,如何基于JS UI框架來開發(fā)呢?
JS UI框架下FA與PA交互的使用場景
通常一個典型使用JS UI框架的HarmonyOS應(yīng)用開發(fā)模型如下圖1所示:

圖1 典型應(yīng)用開發(fā)模型
HarmonyOS的用戶應(yīng)用程序包APP由一個或多個Hap包組成。每個Hap可以包含一個或多個Ability。Ability分為兩種類型:Feature Ability(簡稱FA)和Particle Ability(簡稱PA),F(xiàn)A和PA是HarmonyOS應(yīng)用的基本組成單元,能夠?qū)崿F(xiàn)特定的業(yè)務(wù)功能。FA有UI界面,而PA無UI界面。FA有多種展現(xiàn)形式,如普通界面形式Page Ability,服務(wù)卡片形式Form等,一般使用JS 語言實(shí)現(xiàn)前臺界面。PA支持ServiceAbility和Data Ability。ServiceAbility提供后臺運(yùn)行任務(wù)的能力,如處理復(fù)雜后臺任務(wù)等。Data Ability用于對外部提供統(tǒng)一的數(shù)據(jù)訪問抽象。這兩個Ability一般使用Java 語言實(shí)現(xiàn)。
當(dāng)前HarmonyOS Ability具體分類信息如下:

JS UI框架提供的聲明式編程,使應(yīng)用開發(fā)更加簡單,但當(dāng)前HarmonyOS JS API還不夠豐富,無法處理數(shù)據(jù)等更復(fù)雜的業(yè)務(wù)。為了達(dá)到處理復(fù)雜業(yè)務(wù),同時保證業(yè)務(wù)數(shù)據(jù)和UI的解耦,一般會將復(fù)雜邏輯放到PA中即JAVA端實(shí)現(xiàn),而界面交互則放到FA中的UI部分即JS端實(shí)現(xiàn),如圖1所示。
這就涉及到FA(JS端)與PA(JAVA端)的交互,為此,Harmony OS JS UI框架提供了JS FA(Feature Ability)調(diào)用JAVA PA(Particle Ability)的機(jī)制,該機(jī)制提供了一種通道來傳遞方法調(diào)用、處理數(shù)據(jù)返回以及訂閱事件上報(bào)。
下面我們通過一兩個例子來解釋該方法涉及的技術(shù)原理。
HarmonyOS下FA調(diào)用PA機(jī)制
接口拓展機(jī)制
為支持ACE開發(fā)框架一次開發(fā),跨平臺運(yùn)行的目標(biāo),采用了接口拓展機(jī)制打通前端應(yīng)用層和后端平臺層。JS UI框架提供了一種自動封裝平臺能力擴(kuò)展API的機(jī)制,讓應(yīng)用開發(fā)者輕松調(diào)用API即能完成JS端到JAVA端的傳遞方法調(diào)用、處理數(shù)據(jù)返回以及訂閱事件上報(bào)。

圖2 模塊框架模型
如圖2所示,當(dāng)前HarmonyOS支持JS作為前端應(yīng)用開發(fā)語言,提供API接口,供開發(fā)者實(shí)現(xiàn)業(yè)務(wù)邏輯,通過類似接口拓展機(jī)制將JS層的參數(shù)傳遞到平臺層(Native),同時在平臺層提供插件代碼(Plugin Native code)供三方平臺實(shí)現(xiàn)業(yè)務(wù)邏輯。
JS FrameWork
提供API用于傳遞方法調(diào)用,數(shù)據(jù)流通信,以及訂閱事件回調(diào)。并通過JsBridge橋接起C++ 與JS,JsBridge主要負(fù)責(zé)加載JS代碼,運(yùn)行在QJS Engine上,將JS代碼通過全局函數(shù)橋接到C++ 上,并將C++的結(jié)果返回給JS層。
QJS Engine
QuickJS 是一個輕量且可嵌入的 JavaScript 引擎,包括模塊、異步生成器和代理。
ACE Framework
將JS的消息往平臺層透傳,將JS數(shù)據(jù)轉(zhuǎn)換成C++ 類型的數(shù)據(jù),再通過C++ 與JAVA的JNI接口類,將C++的數(shù)據(jù)傳遞到JAVA側(cè),并接收J(rèn)AVA側(cè)返回的數(shù)據(jù)。
Native
負(fù)責(zé)平臺層數(shù)據(jù)編解碼,并根據(jù)解碼后得到的FunctionName調(diào)用第三方開發(fā)者的插件代碼邏輯。
HarmonyOS API
平臺層提供JAVA端API接口。開發(fā)者實(shí)現(xiàn)JAVA端對應(yīng)接口的業(yè)務(wù)邏輯。
HarmonyOS下FA調(diào)用PA機(jī)制
JS UI框架當(dāng)前提供了Ability和Internal Ability兩種FA調(diào)用PA的方式:
- Ability調(diào)用方式:擁有獨(dú)立的Ability生命周期,F(xiàn)A使用遠(yuǎn)端進(jìn)程通信拉起并請求PA服務(wù),適用于基本服務(wù)PA有多個FA調(diào)用或者PA在后臺獨(dú)立運(yùn)行的場景。
- Internal Ability調(diào)用方式:PA與FA共進(jìn)程,PA和FA采用內(nèi)部函數(shù)調(diào)用的方式進(jìn)行通信,適用于對服務(wù)響應(yīng)時延要求較高的場景。該方式下PA不支持其他FA訪問調(diào)用。
這兩種調(diào)用方式在代碼中可通過abilityType來標(biāo)識,具體使用差異見下表:

FA調(diào)用PA注意事項(xiàng):
- JS和JAVA側(cè)定義好的“方法調(diào)用”在對外開放后,需要保證前向兼容性。
- 序列化數(shù)據(jù)默認(rèn)最大支持200KB數(shù)據(jù)量,若需要傳輸大數(shù)據(jù),可以使用對應(yīng)接口ohos.utils.Parcel.setCapacity()調(diào)整buffer容量大小。
FA調(diào)用PA開發(fā)方法
下面來給大家詳細(xì)介紹JS FA調(diào)用JAVA PA的開發(fā)方法。

圖3 FA調(diào)用PA開發(fā)方法
如圖3所示,當(dāng)FeatureAbility Plugin收到JS調(diào)用請求后,系統(tǒng)根據(jù)開發(fā)者在JS接口中設(shè)置的參數(shù)如指定的abilityType(Ability或Internal Ability),來選擇對應(yīng)的方式進(jìn)行處理。開發(fā)者在onRemoteRequest()中實(shí)現(xiàn)PA提供的業(yè)務(wù)邏輯,不同的業(yè)務(wù)通過業(yè)務(wù)碼來區(qū)分。
FA端
1.Channel JS API提供以下模塊能力:
- ModuleGroup
用于傳遞方法調(diào)用的類,通常用于上層應(yīng)用者調(diào)用native中的某個方法而定義使用。調(diào)用callNative()方法即可將function以及對應(yīng)的參數(shù)傳遞到平臺層,需要在Native層也適配相應(yīng)的邏輯代碼。
簡言之,即ModuleGroup實(shí)現(xiàn)JS調(diào)用JAVA方法,提供的JS API如下:
√ 調(diào)用PA能力,F(xiàn)eatureAbility.callAbility(OBJECT)
- EventGroup
用于數(shù)據(jù)流通信,通常用于平臺層觸發(fā)的Native事件通知應(yīng)用層。在應(yīng)用層調(diào)用subscribe()方法注冊回調(diào)事件啟動監(jiān)聽平臺,調(diào)用unSubscribe()取消平臺監(jiān)聽。第三方開發(fā)者在平臺層需要適配相應(yīng)的邏輯代碼。
簡言之,即EventGroup實(shí)現(xiàn)JAVA回調(diào)JS,提供的JS API如下:
- 訂閱PA能力,F(xiàn)eatureAbility.subscribeAbilityEvent(OBJECT, Function)
- 取消訂閱PA能力,F(xiàn)eatureAbility.unsubscribeAbilityEvent(OBJECT)
2.FeatureAbility Plugin
主要完成方法調(diào)用、數(shù)據(jù)流的參數(shù)傳遞(編解碼)、線程切換、JNI轉(zhuǎn)換等處理。
其主要提供以下2個重點(diǎn)模塊:
- Internal Ability Manager
用于Internal Ability的管理,包括注冊管理等,注冊后的Internal Ability與FA共生命周期。
- Connection Manager
JS端與JAVA端通過接口擴(kuò)展機(jī)制進(jìn)行通信,通過bundleName和abilityName來進(jìn)行關(guān)聯(lián)。
PA端
PA端提供以下兩類接口:
- IRemoteObject.onRemoteRequest(int, MessageParcel, MessageParcel, MessageOption)
Ability調(diào)用方式,F(xiàn)A使用遠(yuǎn)端進(jìn)程通信拉起并請求PA服務(wù)。
- AceInternalAbility.AceInternalAbilityHandler.onRemoteRequest(int, MessageParcel, MessageParcel, MessageOption)
Internal Ability調(diào)用方式,采用內(nèi)部函數(shù)調(diào)用的方式和FA進(jìn)行通信。
Ability調(diào)用方式流程

圖4 JS FA調(diào)用JAVA PA過程(Ability方式)
1.FA JS端指定PA的調(diào)用方式及相關(guān)消息碼和內(nèi)容,調(diào)用PA(訂閱PA類似)。設(shè)置bundleName,abilityName,abilityType等。
2.PA JAVA端響應(yīng):
通過Ability方式拉起的PA繼承自Ability,F(xiàn)A在請求PA服務(wù)時會連接到PA,連接成功后,PA在onConnect返回一個remote對象(RemoteObject),用于FA向PA發(fā)送消息。remote對象實(shí)現(xiàn)onRemoteRequest方法,用于響應(yīng)FA端的請求。
示例代碼如下:e.g.兩數(shù)求和
FA端 (Ability方式)
- // abilityType: 0-Ability; 1-Internal Ability
- const ABILITY_TYPE_EXTERNAL = 0;
- const ABILITY_TYPE_INTERNAL = 1;
- // syncOption(Optional, default sync): 0-Sync; 1-Async
- const ACTION_SYNC = 0;
- const ACTION_ASYNC = 1;
- const ACTION_MESSAGE_CODE_PLUS = 1001;
- export default {
- plus: async function() {
- var actionData = {};
- actionData.firstNum = 1024;
- actionData.secondNum = 2048;
- // 請求參數(shù),abilityName、bundleName、messageCode、abilityType、actionData需要求和的2個入?yún)?nbsp;
- var action = {};
- action.bundleName = 'com.example.hiaceservice';
- action.abilityName = 'com.example.hiaceservice.ComputeServiceAbility';
- action.messageCode = ACTION_MESSAGE_CODE_PLUS;
- action.data = actionData;
- // 使用ability方式
- action.abilityType = ABILITY_TYPE_EXTERNAL;
- action.syncOption = ACTION_SYNC;
- // FA調(diào)用PA
- var result = await FeatureAbility.callAbility(action);
- var ret = JSON.parse(result);
- if (ret.code == 0) {
- console.info('plus result is:' + JSON.stringify(ret.abilityResult));
- } else {
- console.error('plus error code:' + JSON.stringify(ret.code));
- }
- }
- }
PA端(Ability方式)
- public class ComputeServiceAbility extends Ability {
- private MyRemote remote = new MyRemote();
- // FA在請求PA服務(wù)時會連接PA,連接成功后,需要在onConnect返回一個remote對象,供FA向PA發(fā)送消息
- @Override
- protected IRemoteObject onConnect(Intent intent) {
- super.onConnect(intent);
- return remote.asObject();
- }
- // remote對象的實(shí)現(xiàn),完成消息請求處理,回傳
- class MyRemote extends RemoteObject implements IRemoteBroker {
- private static final int SUCCESS = 0;
- private static final int ERROR = 1;
- private static final int PLUS = 1001;
- MyRemote() {
- super("MyService_MyRemote");
- }
- @Override
- public boolean onRemoteRequest(int code, MessageParcel data, MessageParcel reply, MessageOption option) {
- switch (code) {
- // 消息碼PLUS
- case PLUS: {
- // 消息參數(shù)解析
- String dataStr = data.readString();
- RequestParam param = new RequestParam();
- try {
- param = ZSONObject.stringToClass(dataStr, RequestParam.class);
- } catch (RuntimeException e) {
- HiLog.error(LABEL, "convert failed.");
- }
- // 返回結(jié)果設(shè)置
- Map<String, Object> result = new HashMap<String, Object>();
- result.put("code", SUCCESS);
- result.put("abilityResult", param.getFirstNum() + param.getSecondNum());
- // 返回結(jié)果回傳
- reply.writeString(ZSONObject.toZSONString(result));
- break;
- }
- default: {
- Map<String, Object> result = new HashMap<String, Object>();
- result.put("abilityError", ERROR);
- reply.writeString(ZSONObject.toZSONString(result));
- return false;
- }
- }
- return true;
- }
- @Override
- public IRemoteObject asObject() {
- return this;
- }
- }
- }
Internal Ability調(diào)用方式流程

圖5 JS FA調(diào)用JAVA PA過程(Internal Ability方式)
1.FA JS端指定PA的調(diào)用方式及相關(guān)消息碼和內(nèi)容,調(diào)用PA(訂閱PA類似)。設(shè)置bundleName,abilityName,abilityType等。
2. PA JAVA端響應(yīng):
通過Internal Ability方式拉起的PA需要繼承自AceInternalAbility,且需要在AceAbility中注冊該Internal Ability(AceAbility中執(zhí)行register方法)。
PA中通過setInternalAbilityHandler方法注冊onRemoteRequest方法,用于響應(yīng)FA端的請求。
示例代碼如下:e.g.兩數(shù)求和
FA端(Internal Ability方式)
和使用ability方式代碼類似,區(qū)別是需要改變方式類型為Internal Ability:
action.abilityType = ABILITY_TYPE_INTERNAL;
PA端(Internal Ability方式):
- //Internal Ability注冊:在MainAbility注冊
- public class MainAbility extends AceAbility {
- @Override
- public void onStart(Intent intent) {
- // 注冊, 如果需要在Page初始化(onInit或之前)時調(diào)用AceInternalAbility的能力,注冊操作需要在super.onStart之前進(jìn)行
- ComputeInternalAbility.register(this);
- ...
- super.onStart(intent);
- }
- @Override
- public void onStop() {
- // 注銷
- ComputeInternalAbility.unregister();
- super.onStop();
- }
- }
在JAVA目錄下實(shí)現(xiàn)InternalAbility(此處為ComputeInternalAbility)
- public class ComputeInternalAbility extends AceInternalAbility {
- private static final String BUNDLE_NAME = "com.example.hiaceservice";
- private static final String ABILITY_NAME = "com.example.hiaceservice.ComputeInternalAbility";
- private static final int SUCCESS = 0;
- private static final int ERROR = 1;
- private static final int PLUS = 1001;
- private static ComputeInternalAbility instance;
- private AbilityContext abilityContext;
- // 如果多個Ability實(shí)例都需要注冊當(dāng)前InternalAbility實(shí)例,需要更改構(gòu)造函數(shù),設(shè)定自己的bundleName和abilityName
- public ComputeInternalAbility() {
- super(BUNDLE_NAME, ABILITY_NAME);
- }
- public boolean onRemoteRequest(int code, MessageParcel data, MessageParcel reply, MessageOption option) {
- switch (code) {
- case PLUS: {
- String dataStr = data.readString();
- RequestParam param = new RequestParam();
- try {
- param = ZSONObject.stringToClass(dataStr, RequestParam.class);
- } catch (RuntimeException e) {
- HiLog.error(LABEL, "convert failed.");
- }
- // 返回結(jié)果當(dāng)前僅支持String,對于復(fù)雜結(jié)構(gòu)可以序列化為ZSON字符串上報(bào)
- Map<String, Object> result = new HashMap<String, Object>();
- result.put("code", SUCCESS);
- result.put("abilityResult", param.getFirstNum() + param.getSecondNum());
- // SYNC
- if (option.getFlags() == MessageOption.TF_SYNC) {
- reply.writeString(ZSONObject.toZSONString(result));
- } else {
- // ASYNC
- MessageParcel responseData = MessageParcel.obtain();
- responseData.writeString(ZSONObject.toZSONString(result));
- IRemoteObject remoteReply = reply.readRemoteObject();
- try {
- remoteReply.sendRequest(0, responseData, MessageParcel.obtain(), new MessageOption());
- } catch (RemoteException exception) {
- return false;
- } finally {
- responseData.reclaim();
- }
- }
- break;
- }
- default: {
- Map<String, Object> result = new HashMap<String, Object>();
- result.put("abilityError", ERROR);
- reply.writeString(ZSONObject.toZSONString(result));
- return false;
- }
- }
- return true;
- }
- /** * Internal ability 注冊接口。 */
- public static void register(AbilityContext abilityContext) {
- instance = new ComputeInternalAbility();
- instance.onRegister(abilityContext);
- }
- private void onRegister(AbilityContext abilityContext) {
- this.abilityContext = abilityContext;
- this.setInternalAbilityHandler((code, data, reply, option) -> {
- return this.onRemoteRequest(code, data, reply, option);
- });
- }
- /** * Internal ability 注銷接口。 */
- public static void unregister() {
- instance.onUnregister();
- }
- private void onUnregister() {
- abilityContext = null;
- this.setInternalAbilityHandler(null);
- }
- }
以上代碼樣例只是關(guān)鍵部分,完整代碼樣例可參考官網(wǎng)資料【JS FA如何調(diào)用PA】:
JS FA如何調(diào)用PA
https://developer.harmonyos.com/cn/docs/documentation/doc-guides/ui-js-fa-call-pa-0000001050435961
本文介紹了JS FA(Feature Ability)調(diào)用JAVA PA(Particle Ability)的機(jī)制和使用場景,基于JS UI框架開發(fā)的應(yīng)用可以使用該機(jī)制完成更豐富的功能。未來HarmonyOS會持續(xù)豐富完善JS API,為開發(fā)者提供更便捷的API能力。
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)