HarmonyOS-鸿蒙app开发 —JS FA调用Java PA机制

HarmonyOS-鸿蒙app开发 —JS FA调用Java PA机制

使用兼容JS的类Web开发范式的方舟开发框架提供了JS FA(Feature Ability)调用Java PA(Particle Ability)的机制,该机制提供了一种通道来传递方法调用、处理数据返回以及订阅事件上报,支持的UI页面和组件请参考构建JS用户界面

当前提供Ability和Internal Ability两种调用方式,开发者可以根据业务场景选择合适的调用方式进行开发。

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

    对于Internal Ability调用方式的开发,可以使用js2java-codegen工具自动生成代码,提高开发效率。

JS端与Java端通过bundleName和abilityName来进行关联。在系统收到JS调用请求后,根据开发者在JS接口中设置的参数来选择对应的处理方式。开发者在onRemoteRequest()中实现PA提供的业务逻辑。详细信息请参考JS FA调用Java PA机制

FA调用PA接口

FA端提供以下三个JS接口:

  • FeatureAbility.callAbility(OBJECT):调用PA能力。
  • FeatureAbility.subscribeAbilityEvent(OBJECT, Function):订阅PA能力。
  • FeatureAbility.unsubscribeAbilityEvent(OBJECT):取消订阅PA能力。

PA端提供以下两类接口:

  • IRemoteObject.onRemoteRequest(int, MessageParcel, MessageParcel, MessageOption):Ability调用方式,FA使用远端进程通信拉起并请求PA服务。
  • AceInternalAbility.AceInternalAbilityHandler.onRemoteRequest(int, MessageParcel, MessageParcel, MessageOption):Internal Ability调用方式,采用内部函数调用的方式和FA进行通信。

FA调用PA常见问题

  • callAbility返回报错:”Internal ability not register.”

    返回该错误说明JS接口调用请求未在系统中找到对应的InternalAbilityHandler进行处理,因此需要检查以下几点是否正确执行:

    1. 在AceAbility继承类中对AceInternalAbility继承类执行了register方法,具体注册可参考Internal Ability的示例代码。
    2. JS侧填写的bundleName和abilityName与AceInternalAbility继承类构造函数中填写的名称保持相同,大小写敏感。
    3. 检查JS端填写的abilityType(0:Ability; 1:Internal Ability),确保没有将AbilityType缺省或误填写为Ability方式。

    Ability和Internal Ability是两种不同的FA调用PA的方式。表1列举了在开发时各方面的差异,供开发者参考,避免开发时将两者混淆使用:

    表1 Ability和InternalAbility差异项

    差异项

    Ability

    InternalAbility

    JS端(abilityType)

    0

    1

    是否需要在config.json的abilities中为PA添加声明

    需要(有独立的生命周期)

    不需要(和FA共生命周期)

    是否需要在FA中注册

    不需要

    需要

    继承的类

    ohos.aafwk.ability.Ability

    ohos.ace.ability.AceInternalAbility

    是否允许被其他FA访问调用

  • FeatureAbility.callAbility中syncOption参数说明:
    • 对于JS FA侧,返回的结果都是Promise对象,因此无论该参数取何值,都采用异步方式等待PA侧响应。
    • 对于JAVA PA侧,在Internal Ability方式下收到FA的请求后,根据该参数的取值来选择:通过同步的方式获取结果后返回;或者异步执行PA逻辑,获取结果后使用remoteObject.sendRequest的方式将结果返回FA。
  • 使用await方式调用时IDE编译报错,需引入babel-runtime/regenerator。

示例参考

JS端调用FeatureAbility接口,传入两个Number参数,Java端接收后返回两个数的和。

JS FA应用的JS模块(entry/src/main)的典型开发目录结构如下:

图1 目录结构
  • FA JavaScript端
    使用Internal Ability方式时,需要将对应的action.abilityType值改为ABILITY_TYPE_INTERNAL。

// 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;

var action = {};
action.bundleName = ‘com.example.hiaceservice’;
action.abilityName = ‘com.example.hiaceservice.ComputeServiceAbility’;
action.messageCode = ACTION_MESSAGE_CODE_PLUS;
action.data = actionData;
action.abilityType = ABILITY_TYPE_EXTERNAL;
action.syncOption = ACTION_SYNC;

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方式)

    功能代码实现:

    在java目录下新建一个Service Ability,文件命名为ComputeServiceAbility.java

    1. package com.example.hiaceservice;
    2. // ohos相关接口包
    3. import ohos.aafwk.ability.Ability;
    4. import ohos.aafwk.content.Intent;
    5. import ohos.hiviewdfx.HiLog;
    6. import ohos.hiviewdfx.HiLogLabel;
    7. import ohos.rpc.IRemoteBroker;
    8. import ohos.rpc.IRemoteObject;
    9. import ohos.rpc.RemoteObject;
    10. import ohos.rpc.MessageParcel;
    11. import ohos.rpc.MessageOption;
    12. import ohos.utils.zson.ZSONObject;
    13. import java.util.HashMap;
    14. import java.util.Map;
    15. public class ComputeServiceAbility extends Ability {
    16. // 定义日志标签
    17. private static final HiLogLabel LABEL = new HiLogLabel(HiLog.LOG_APP, 0, “MY_TAG”);
    18. private MyRemote remote = new MyRemote();
    19. // FA在请求PA服务时会调用Ability.connectAbility连接PA,连接成功后,需要在onConnect返回一个remote对象,供FA向PA发送消息
    20. @Override
    21. protected IRemoteObject onConnect(Intent intent) {
    22. super.onConnect(intent);
    23. return remote.asObject();
    24. }
    25. class MyRemote extends RemoteObject implements IRemoteBroker {
    26. private static final int SUCCESS = 0;
    27. private static final int ERROR = 1;
    28. private static final int PLUS = 1001;
    29. MyRemote() {
    30. super(“MyService_MyRemote”);
    31. }
    32. @Override
    33. public boolean onRemoteRequest(int code, MessageParcel data, MessageParcel reply, MessageOption option) {
    34. switch (code) {
    35. case PLUS: {
    36. String dataStr = data.readString();
    37. RequestParam param = new RequestParam();
    38. try {
    39. param = ZSONObject.stringToClass(dataStr, RequestParam.class);
    40. } catch (RuntimeException e) {
    41. HiLog.error(LABEL, “convert failed.”);
    42. }
    43. // 返回结果当前仅支持String,对于复杂结构可以序列化为ZSON字符串上报
    44. Map<String, Object> result = new HashMap<String, Object>();
    45. result.put(“code”, SUCCESS);
    46. result.put(“abilityResult”, param.getFirstNum() + param.getSecondNum());
    47. reply.writeString(ZSONObject.toZSONString(result));
    48. break;
    49. }
    50. default: {
    51. Map<String, Object> result = new HashMap<String, Object>();
    52. result.put(“abilityError”, ERROR);
    53. reply.writeString(ZSONObject.toZSONString(result));
    54. return false;
    55. }
    56. }
    57. return true;
    58. }
    59. @Override
    60. public IRemoteObject asObject() {
    61. return this;
    62. }
    63. }
    64. }

    请求参数代码:

    RequestParam.java

    1. public class RequestParam {
    2. private int firstNum;
    3. private int secondNum;
    4. public int getFirstNum() {
    5. return firstNum;
    6. }
    7. public void setFirstNum(int firstNum) {
    8. this.firstNum = firstNum;
    9. }
    10. public int getSecondNum() {
    11. return secondNum;
    12. }
    13. public void setSecondNum(int secondNum) {
    14. this.secondNum = secondNum;
    15. }
    16. }
  • PA端(Internal Ability方式)

    功能代码实现(以下代码可以使用js2java-codegen工具自动生成):

    在java目录下新建一个Service Ability,文件命名为ComputeInternalAbility.java

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

    Internal Ability注册:修改继承AceAbility工程中的代码

    1. public class MainAbility extends AceAbility {
    2. @Override
    3. public void onStart(Intent intent) {
    4. // 注册, 如果需要在Page初始化(onInit或之前)时调用AceInternalAbility的能力,注册操作需要在super.onStart之前进行
    5. ComputeInternalAbility.register(this);
    6. super.onStart(intent);
    7. }
    8. @Override
    9. public void onStop() {
    10. // 注销
    11. ComputeInternalAbility.unregister();
    12. super.onStop();
    13. }
    14. }
5 1 投票
文章评分
订阅评论
提醒
0 评论
最旧
最新 最多投票
内联反馈
查看所有评论
0
希望看到您的想法,请您发表评论x
🚀 如未找到文章请搜索栏搜素 | Ctrl+D收藏本站 | 联系邮箱:15810050733@qq.com