JS UI框架下FA与PA是如何交互的

开发
本文介绍了JS FA(Feature Ability)调用JAVA PA(Particle Ability)的机制和使用场景,基于JS UI框架开发的应用可以使用该机制完成更丰富的功能。

[[411492]]

想了解更多内容,请访问:

51CTO和华为官方合作共建的鸿蒙技术社区

https://harmonyos.51cto.com

上一篇“HarmonyOS UI框架关键技术解析”中,给大家介绍了基于JS/JAVA UI框架开发简单业务的方法。那么对于复杂业务,在当前HarmonyOS版本下,如何基于JS UI框架来开发呢?

JS UI框架下FA与PA交互的使用场景

通常一个典型使用JS UI框架的HarmonyOS应用开发模型如下图1所示:

JS UI框架下FA与PA是如何交互的-鸿蒙HarmonyOS技术社区

图1 典型应用开发模型

HarmonyOS的用户应用程序包APP由一个或多个Hap包组成。每个Hap可以包含一个或多个Ability。Ability分为两种类型:Feature Ability(简称FA)和Particle Ability(简称PA),FA和PA是HarmonyOS应用的基本组成单元,能够实现特定的业务功能。FA有UI界面,而PA无UI界面。FA有多种展现形式,如普通界面形式Page Ability,服务卡片形式Form等,一般使用JS 语言实现前台界面。PA支持ServiceAbility和Data Ability。ServiceAbility提供后台运行任务的能力,如处理复杂后台任务等。Data Ability用于对外部提供统一的数据访问抽象。这两个Ability一般使用Java 语言实现。

当前HarmonyOS Ability具体分类信息如下:

JS UI框架下FA与PA是如何交互的-鸿蒙HarmonyOS技术社区

JS UI框架提供的声明式编程,使应用开发更加简单,但当前HarmonyOS JS API还不够丰富,无法处理数据等更复杂的业务。为了达到处理复杂业务,同时保证业务数据和UI的解耦,一般会将复杂逻辑放到PA中即JAVA端实现,而界面交互则放到FA中的UI部分即JS端实现,如图1所示。

这就涉及到FA(JS端)与PA(JAVA端)的交互,为此,Harmony OS JS UI框架提供了JS FA(Feature Ability)调用JAVA PA(Particle Ability)的机制,该机制提供了一种通道来传递方法调用、处理数据返回以及订阅事件上报。

下面我们通过一两个例子来解释该方法涉及的技术原理。

HarmonyOS下FA调用PA机制

接口拓展机制

为支持ACE开发框架一次开发,跨平台运行的目标,采用了接口拓展机制打通前端应用层和后端平台层。JS UI框架提供了一种自动封装平台能力扩展API的机制,让应用开发者轻松调用API即能完成JS端到JAVA端的传递方法调用、处理数据返回以及订阅事件上报。

JS UI框架下FA与PA是如何交互的-鸿蒙HarmonyOS技术社区

图2 模块框架模型

如图2所示,当前HarmonyOS支持JS作为前端应用开发语言,提供API接口,供开发者实现业务逻辑,通过类似接口拓展机制将JS层的参数传递到平台层(Native),同时在平台层提供插件代码(Plugin Native code)供三方平台实现业务逻辑。

 JS FrameWork

提供API用于传递方法调用,数据流通信,以及订阅事件回调。并通过JsBridge桥接起C++ 与JS,JsBridge主要负责加载JS代码,运行在QJS Engine上,将JS代码通过全局函数桥接到C++ 上,并将C++的结果返回给JS层。

 QJS Engine

QuickJS 是一个轻量且可嵌入的 JavaScript 引擎,包括模块、异步生成器和代理。

 ACE Framework

将JS的消息往平台层透传,将JS数据转换成C++ 类型的数据,再通过C++ 与JAVA的JNI接口类,将C++的数据传递到JAVA侧,并接收JAVA侧返回的数据。

 Native

负责平台层数据编解码,并根据解码后得到的FunctionName调用第三方开发者的插件代码逻辑。

 HarmonyOS API

平台层提供JAVA端API接口。开发者实现JAVA端对应接口的业务逻辑。

HarmonyOS下FA调用PA机制

JS UI框架当前提供了Ability和Internal Ability两种FA调用PA的方式:

  •  Ability调用方式:拥有独立的Ability生命周期,FA使用远端进程通信拉起并请求PA服务,适用于基本服务PA有多个FA调用或者PA在后台独立运行的场景。
  •  Internal Ability调用方式:PA与FA共进程,PA和FA采用内部函数调用的方式进行通信,适用于对服务响应时延要求较高的场景。该方式下PA不支持其他FA访问调用。

这两种调用方式在代码中可通过abilityType来标识,具体使用差异见下表:

JS UI框架下FA与PA是如何交互的-鸿蒙HarmonyOS技术社区

FA调用PA注意事项:

  •  JS和JAVA侧定义好的“方法调用”在对外开放后,需要保证前向兼容性。
  •  序列化数据默认最大支持200KB数据量,若需要传输大数据,可以使用对应接口ohos.utils.Parcel.setCapacity()调整buffer容量大小。

FA调用PA开发方法

下面来给大家详细介绍JS FA调用JAVA PA的开发方法。

JS UI框架下FA与PA是如何交互的-鸿蒙HarmonyOS技术社区

图3 FA调用PA开发方法

如图3所示,当FeatureAbility Plugin收到JS调用请求后,系统根据开发者在JS接口中设置的参数如指定的abilityType(Ability或Internal Ability),来选择对应的方式进行处理。开发者在onRemoteRequest()中实现PA提供的业务逻辑,不同的业务通过业务码来区分。

 FA端

1.Channel JS API提供以下模块能力:

  •  ModuleGroup

用于传递方法调用的类,通常用于上层应用者调用native中的某个方法而定义使用。调用callNative()方法即可将function以及对应的参数传递到平台层,需要在Native层也适配相应的逻辑代码。

简言之,即ModuleGroup实现JS调用JAVA方法,提供的JS API如下:

√ 调用PA能力,FeatureAbility.callAbility(OBJECT)

  •  EventGroup

用于数据流通信,通常用于平台层触发的Native事件通知应用层。在应用层调用subscribe()方法注册回调事件启动监听平台,调用unSubscribe()取消平台监听。第三方开发者在平台层需要适配相应的逻辑代码。

简言之,即EventGroup实现JAVA回调JS,提供的JS API如下:

  • 订阅PA能力,FeatureAbility.subscribeAbilityEvent(OBJECT, Function)
  •  取消订阅PA能力,FeatureAbility.unsubscribeAbilityEvent(OBJECT)

2.FeatureAbility Plugin

主要完成方法调用、数据流的参数传递(编解码)、线程切换、JNI转换等处理。

其主要提供以下2个重点模块:

  •  Internal Ability Manager

用于Internal Ability的管理,包括注册管理等,注册后的Internal Ability与FA共生命周期。

  •  Connection Manager

JS端与JAVA端通过接口扩展机制进行通信,通过bundleName和abilityName来进行关联。

 PA端

PA端提供以下两类接口:

  •  IRemoteObject.onRemoteRequest(int, MessageParcel, MessageParcel, MessageOption)

Ability调用方式,FA使用远端进程通信拉起并请求PA服务。

  •  AceInternalAbility.AceInternalAbilityHandler.onRemoteRequest(int, MessageParcel, MessageParcel, MessageOption)

Internal Ability调用方式,采用内部函数调用的方式和FA进行通信。

Ability调用方式流程

JS UI框架下FA与PA是如何交互的-鸿蒙HarmonyOS技术社区

图4 JS FA调用JAVA PA过程(Ability方式)

1.FA JS端指定PA的调用方式及相关消息码和内容,调用PA(订阅PA类似)。设置bundleName,abilityName,abilityType等。

2.PA JAVA端响应:

通过Ability方式拉起的PA继承自Ability,FA在请求PA服务时会连接到PA,连接成功后,PA在onConnect返回一个remote对象(RemoteObject),用于FA向PA发送消息。remote对象实现onRemoteRequest方法,用于响应FA端的请求。

示例代码如下:e.g.两数求和

 FA端 (Ability方式)

  1. // abilityType: 0-Ability; 1-Internal Ability 
  2. const ABILITY_TYPE_EXTERNAL = 0; 
  3. const ABILITY_TYPE_INTERNAL = 1; 
  4. // syncOption(Optional, default sync): 0-Sync; 1-Async 
  5. const ACTION_SYNC = 0; 
  6. const ACTION_ASYNC = 1; 
  7. const ACTION_MESSAGE_CODE_PLUS = 1001; 
  8. export default {   
  9.   plus: async function() {     
  10.     var actionData = {};     
  11.     actionData.firstNum = 1024;     
  12.     actionData.secondNum = 2048; 
  13.      
  14.     // 请求参数,abilityName、bundleName、messageCode、abilityType、actionData需要求和的2个入参     
  15.     var action = {};     
  16.     action.bundleName = 'com.example.hiaceservice';     
  17.     action.abilityName = 'com.example.hiaceservice.ComputeServiceAbility';     
  18.     action.messageCode = ACTION_MESSAGE_CODE_PLUS; 
  19.     action.data = actionData; 
  20.     // 使用ability方式     
  21.     action.abilityType = ABILITY_TYPE_EXTERNAL;     
  22.     action.syncOption = ACTION_SYNC; 
  23.      
  24.     // FA调用PA     
  25.     var result = await FeatureAbility.callAbility(action);     
  26.     var ret = JSON.parse(result);     
  27.     if (ret.code == 0) {       
  28.       console.info('plus result is:' + JSON.stringify(ret.abilityResult));     
  29.     } else {       
  30.       console.error('plus error code:' + JSON.stringify(ret.code)); 
  31.     }   
  32.   } 

PA端(Ability方式)

  1. public class ComputeServiceAbility extends Ability {   
  2.   private MyRemote remote = new MyRemote();   
  3.   // FA在请求PA服务时会连接PA,连接成功后,需要在onConnect返回一个remote对象,供FA向PA发送消息   
  4.   @Override   
  5.   protected IRemoteObject onConnect(Intent intent) {     
  6.     super.onConnect(intent);     
  7.     return remote.asObject();   
  8.   } 
  9.   // remote对象的实现,完成消息请求处理,回传   
  10.   class MyRemote extends RemoteObject implements IRemoteBroker {     
  11.     private static final int SUCCESS = 0;     
  12.     private static final int ERROR = 1;     
  13.     private static final int PLUS = 1001;        
  14.      
  15.     MyRemote() {       
  16.       super("MyService_MyRemote");     
  17.     }     
  18.     @Override     
  19.     public boolean onRemoteRequest(int code, MessageParcel data, MessageParcel reply, MessageOption option) {       
  20.       switch (code) { 
  21.       // 消息码PLUS         
  22.       case PLUS: { 
  23.         // 消息参数解析           
  24.         String dataStr = data.readString();           
  25.         RequestParam param = new RequestParam();           
  26.         try {                   
  27.           param = ZSONObject.stringToClass(dataStr, RequestParam.class);           
  28.         } catch (RuntimeException e) { 
  29.           HiLog.error(LABEL, "convert failed.");           
  30.         }           
  31.          
  32.         // 返回结果设置           
  33.         Map<String, Object> result = new HashMap<String, Object>();           
  34.         result.put("code", SUCCESS);           
  35.         result.put("abilityResult", param.getFirstNum() + param.getSecondNum());           
  36.         // 返回结果回传           
  37.         reply.writeString(ZSONObject.toZSONString(result));           
  38.         break;         
  39.       }         
  40.       default: {           
  41.         Map<String, Object> result = new HashMap<String, Object>(); 
  42.         result.put("abilityError", ERROR);           
  43.         reply.writeString(ZSONObject.toZSONString(result));           
  44.         return false;         
  45.       }       
  46.     }       
  47.     return true;     
  48.   }     
  49.   @Override     
  50.   public IRemoteObject asObject() {       
  51.      return this;     
  52.    }  
  53.   } 

Internal Ability调用方式流程

JS UI框架下FA与PA是如何交互的-鸿蒙HarmonyOS技术社区

图5 JS FA调用JAVA PA过程(Internal Ability方式)

1.FA JS端指定PA的调用方式及相关消息码和内容,调用PA(订阅PA类似)。设置bundleName,abilityName,abilityType等。

2. PA JAVA端响应:

通过Internal Ability方式拉起的PA需要继承自AceInternalAbility,且需要在AceAbility中注册该Internal Ability(AceAbility中执行register方法)。

PA中通过setInternalAbilityHandler方法注册onRemoteRequest方法,用于响应FA端的请求。

示例代码如下:e.g.两数求和

FA端(Internal Ability方式)

和使用ability方式代码类似,区别是需要改变方式类型为Internal Ability:

action.abilityType = ABILITY_TYPE_INTERNAL;

 PA端(Internal Ability方式):

  1. //Internal Ability注册:在MainAbility注册 
  2. public class MainAbility extends AceAbility {   
  3.   @Override   
  4.   public void onStart(Intent intent) {     
  5.     // 注册, 如果需要在Page初始化(onInit或之前)时调用AceInternalAbility的能力,注册操作需要在super.onStart之前进行     
  6.     ComputeInternalAbility.register(this);     
  7.     ...     
  8.     super.onStart(intent);   
  9.   }   
  10.   @Override    
  11.   public void onStop() {     
  12.     // 注销     
  13.     ComputeInternalAbility.unregister();      
  14.     super.onStop();   
  15.   } 

在JAVA目录下实现InternalAbility(此处为ComputeInternalAbility)

  1. public class ComputeInternalAbility extends AceInternalAbility {   
  2.   private static final String BUNDLE_NAME = "com.example.hiaceservice";   
  3.   private static final String ABILITY_NAME = "com.example.hiaceservice.ComputeInternalAbility";   
  4.   private static final int SUCCESS = 0;   
  5.   private static final int ERROR = 1;   
  6.   private static final int PLUS = 1001;   
  7.    
  8.   private static ComputeInternalAbility instance; 
  9.   private AbilityContext abilityContext;   
  10.   // 如果多个Ability实例都需要注册当前InternalAbility实例,需要更改构造函数,设定自己的bundleName和abilityName   
  11.   public ComputeInternalAbility() {     
  12.     super(BUNDLE_NAME, ABILITY_NAME);   
  13.   }   
  14.   public boolean onRemoteRequest(int code, MessageParcel data, MessageParcel reply, MessageOption option) {     
  15.     switch (code) {       
  16.     case PLUS: {         
  17.       String dataStr = data.readString();         
  18.       RequestParam param = new RequestParam();         
  19.       try {                 
  20.         param = ZSONObject.stringToClass(dataStr, RequestParam.class);         
  21.       } catch (RuntimeException e) {                 
  22.         HiLog.error(LABEL, "convert failed.");         
  23.       }         
  24.        
  25.       // 返回结果当前仅支持String,对于复杂结构可以序列化为ZSON字符串上报         
  26.       Map<String, Object> result = new HashMap<String, Object>();         
  27.       result.put("code", SUCCESS);         
  28.       result.put("abilityResult", param.getFirstNum() + param.getSecondNum());         
  29.       // SYNC         
  30.       if (option.getFlags() == MessageOption.TF_SYNC) {           
  31.         reply.writeString(ZSONObject.toZSONString(result));         
  32.       } else {           
  33.       // ASYNC           
  34.         MessageParcel responseData = MessageParcel.obtain();           
  35.         responseData.writeString(ZSONObject.toZSONString(result));           
  36.         IRemoteObject remoteReply = reply.readRemoteObject();           
  37.         try {             
  38.           remoteReply.sendRequest(0, responseData, MessageParcel.obtain(), new MessageOption());           
  39.         } catch (RemoteException exception) { 
  40.           return false;           
  41.         } finally {               
  42.           responseData.reclaim();           
  43.         }         
  44.       }         
  45.       break;       
  46.     }       
  47.     default: {         
  48.       Map<String, Object> result = new HashMap<String, Object>();         
  49.       result.put("abilityError", ERROR);         
  50.       reply.writeString(ZSONObject.toZSONString(result));         
  51.       return false;       
  52.     }     
  53.   }     
  54.   return true;   
  55. }   
  56.  
  57. /**  * Internal ability 注册接口。  */   
  58.   public static void register(AbilityContext abilityContext) {     
  59.     instance = new ComputeInternalAbility();     
  60.     instance.onRegister(abilityContext);   
  61.   }   
  62.   private void onRegister(AbilityContext abilityContext) {     
  63.     this.abilityContext = abilityContext;     
  64.     this.setInternalAbilityHandler((code, data, reply, option) -> {    
  65.     return this.onRemoteRequest(code, data, reply, option);     
  66.     });   
  67.   }   
  68. /**   * Internal ability 注销接口。   */   
  69.   public static void unregister() {     
  70.     instance.onUnregister();   
  71.   }   
  72.   private void onUnregister() {     
  73.     abilityContext = null;     
  74.     this.setInternalAbilityHandler(null);   
  75.   } 

以上代码样例只是关键部分,完整代码样例可参考官网资料【JS FA如何调用PA】:

JS FA如何调用PA

https://developer.harmonyos.com/cn/docs/documentation/doc-guides/ui-js-fa-call-pa-0000001050435961

本文介绍了JS FA(Feature Ability)调用JAVA PA(Particle Ability)的机制和使用场景,基于JS UI框架开发的应用可以使用该机制完成更丰富的功能。未来HarmonyOS会持续丰富完善JS API,为开发者提供更便捷的API能力。

想了解更多内容,请访问:

51CTO和华为官方合作共建的鸿蒙技术社区

https://harmonyos.51cto.com

 

责任编辑:jianghua 来源: 鸿蒙社区
相关推荐

2021-09-10 15:13:41

鸿蒙HarmonyOS应用

2021-10-27 10:05:55

鸿蒙HarmonyOS应用

2022-03-07 14:58:10

ArkUIJS FAJava

2021-10-14 09:53:38

鸿蒙HarmonyOS应用

2022-06-02 14:27:05

UI框架JS

2022-09-08 15:18:51

Ability鸿蒙

2021-12-20 10:07:39

鸿蒙HarmonyOS应用

2015-08-17 09:46:15

UIjs

2010-09-14 09:38:48

AndroidUI

2022-11-11 10:56:37

2020-02-20 15:19:56

JavaSQL注入

2022-05-16 11:17:01

应用开发JSJAVA

2014-07-21 17:48:09

PhoneGapCordovaHtml5

2022-10-27 16:01:41

AbilityStage模型FA模型

2009-06-01 10:47:32

jboss seam例jboss seam开jboss seam

2011-07-01 11:02:30

EnyowebOShello world

2011-04-22 11:24:13

mootools

2011-09-08 10:32:27

Node.js

2021-03-25 15:54:14

鸿蒙HarmonyOS应用开发

2019-04-08 15:11:12

HTTP协议Web
点赞
收藏

51CTO技术栈公众号