鸿蒙OS JS FA如何调用PA
JS UI 框架提供了 JS FA(Feature Ability)调用 Java PA(Particle Ability)的机制,该机制提供了一种通道来传递方法调用、数据返回以及订阅事件上报。
当前提供 Ability 和 Internal Ability 两种调用方式,开发者可以根据业务场景选择合适的调用方式进行开发。
- Ability:拥有独立的 Ability 生命周期,FA 使用远端进程通信拉起并请求 PA 服务,适用于基本服务供多 FA 调用或者服务在后台独立运行的场景。
- Internal Ability:与 FA 共进程,采用内部函数调用的方式和 FA 进行通信,适用于对服务响应时延要求较高的场景。该方式下 PA 不支持其他 FA 访问调用。
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 端提供以下两类接口:
- boolean IRemoteObject.onRemoteRequest(int code, MessageParcel data, MessageParcel reply, MessageOption option):Ability 调用方式,FA 使用远端进程通信拉起并请求PA服务。
- boolean AceInternalAbility.AceInternalAbilityHandler.onRemoteRequest(int code, MessageParcel data, MessageParcel reply, MessageOption option):Internal Ability 调用方式,采用内部函数调用的方式和 FA 进行通信。
FA 调用 PA 常见问题
- callAbility返回报错:"Internal ability not register."
返回该错误说明 JS 接口调用请求未在系统中找到对应的 InternalAbilityHandler 进行处理,因此需要检查以下几点是否正确执行:
- 在 AceAbility 继承类中对 AceInternalAbility 继承类执行了 register 方法,具体注册可参考 Internal Ability 的示例代码。
- JS 侧填写的 bundleName 和 abilityName 与 AceInternalAbility 继承类构造函数中填写的名称保持相同,大小写敏感。
- 检查 JS 端填写的 abilityType(0:Ability; 1:Internal Ability),确保没有将 Ability 误填写为 Internal Ability 方式。
Ability 和 Internal Ability 是两种不同的 FA 调用 PA 的方式。 [表1]列举了在开发时各方面的差异,供开发者参考,避免开发时将两者混淆使用:
差异项 | 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,具体请参见接口通用规则。
示例参考
- FA JavaScript端
// 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.huawei.hiaceservice';
action.abilityName = 'CalcServiceAbility';
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 && ret.code == 0) {
console.info('plus result is:' + JSON.stringify(ret.abilityResult));
} else {
if (ret.code) {
console.error('plus error code:' + JSON.stringify(ret.code));
} else {
console.error('plus error undefined.');
}
}
}
}
- PA 端(Ability 方式)
功能代码实现:
CalcServiceAbility.java
// ohos相关接口包
import ohos.aafwk.ability.Ability;
import ohos.aafwk.content.Intent;
import ohos.rpc.IRemoteBroker;
import ohos.rpc.IRemoteObject;
import ohos.rpc.RemoteObject;
import ohos.rpc.MessageParcel;
import ohos.rpc.MessageOption;
import ohos.utils.zson.ZSONObject;
import java.util.HashMap;
import java.util.Map;
public class CalcServiceAbility extends Ability {
private static final String TAG = "CalcServiceAbility";
private MyRemote remote = new MyRemote();
// FA在请求PA服务时会调用AbilityconnectAbility连接PA,连接成功后,需要在onConnect返回一个remote对象,供FA向PA发送消息
@Override
protected IRemoteObject onConnect(Intent intent) {
super.onConnect(intent);
return remote.asObject();
}
class MyRemote extends RemoteObject implements IRemoteBroker {
private static final int ERROR = -1;
private static final int SUCCESS = 0;
private static final int PLUS = 1001;
MyRemote() {
super("MyService_MyRemote");
}
@Override
public boolean onRemoteRequest(int code, MessageParcel data, MessageParcel reply, MessageOption option) {
switch (code) {
case PLUS: {
String zsonStr = data.readString();
RequestParam param = ZSONObject.stringToClass(zsonStr, RequestParam.class);
// 返回结果仅支持可序列化的Object类型
Map<String, Object> zsonResult = new HashMap<String, Object>();
zsonResult.put("code", SUCCESS);
zsonResult.put("abilityResult", param.getFirstNum() + param.getSecondNum());
reply.writeString(ZSONObject.toZSONString(zsonResult));
break;
}
default: {
Map<String, Object> zsonResult = new HashMap<String, Object>();
zsonResult.put("abilityError", ERROR);
reply.writeString(ZSONObject.toZSONString(zsonResult));
return false;
}
}
return true;
}
@Override
public IRemoteObject asObject() {
return this;
}
}
}
请求参数代码:
RequestParam.java
public class RequestParam {
private int firstNum;
private int secondNum;
public int getFirstNum() {
return firstNum;
}
public void setFirstNum(int firstNum) {
this.firstNum = firstNum;
}
public int getSecondNum() {
return secondNum;
}
public void setSecondNum(int secondNum) {
this.secondNum = secondNum;
}
}
- PA 端(Internal Ability方式)
功能代码实现:
CalcInternalAbility.java
// ohos相关接口包
import ohos.ace.ability.AceInternalAbility;
import ohos.app.AbilityContext;
import ohos.rpc.IRemoteObject;
import ohos.rpc.MessageOption;
import ohos.rpc.MessageParcel;
import ohos.rpc.RemoteException;
import ohos.utils.zson.ZSONObject;
import java.util.HashMap;
import java.util.Map;
public class CalcInternalAbility extends AceInternalAbility {
private static final String TAG = CalcInternalAbility.class.getSimpleName();
private static final String BUNDLE_NAME = "com.huawei.hiaceservice";
private static final String ABILITY_NAME = "CalcInternalAbility";
private static final int ERROR = -1;
private static final int SUCCESS = 0;
private static final int PLUS = 1001;
private static CalcInternalAbility instance;
private AbilityContext abilityContext;
// 如果多个Ability实例都需要注册当前InternalAbility实例,需要更改构造函数,设定自己的bundleName和abilityName
public CalcInternalAbility() {
super(BUNDLE_NAME, ABILITY_NAME);
}
public boolean onRemoteRequest(int code, MessageParcel data, MessageParcel reply, MessageOption option) {
switch (code) {
case PLUS: {
String zsonStr = data.readString();
RequestParam param = ZSONObject.stringToClass(zsonStr, RequestParam.class);
// 返回结果当前仅支持String,对于复杂结构可以序列化为ZSON字符串上报
Map<String, Object> zsonResult = new HashMap<String, Object>();
zsonResult.put("code", SUCCESS);
zsonResult.put("abilityResult", param.getFirstNum() + param.getSecondNum());
// SYNC
if (option.getFlags() == MessageOption.TF_SYNC) {
reply.writeString(ZSONObject.toZSONString(zsonResult));
} else {
// ASYNC
MessageParcel reponseData = MessageParcel.obtain();
reponseData.writeString(ZSONObject.toZSONString(zsonResult));
IRemoteObject remoteReply = reply.readRemoteObject();
try {
remoteReply.sendRequest(0, reponseData, MessageParcel.obtain(), new MessageOption());
reponseData.reclaim();
} catch (RemoteException exception) {
return false;
}
}
break;
}
default: {
Map<String, Object> zsonResult = new HashMap<String, Object>();
zsonResult.put("abilityError", ERROR);
reply.writeString(ZSONObject.toZSONString(zsonResult));
return false;
}
}
return true;
}
/**
* Internal ability registration.
*/
public static void register(AbilityContext abilityContext) {
instance = new CalcInternalAbility();
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 deregistration.
*/
public static void deregister() {
instance.onDeregister();
}
private void onDeregister() {
abilityContext = null;
this.setInternalAbilityHandler(null);
}
}
Internal Ability 注册:修改继承 AceAbility 工程中的代码
public class HiAceInternalAbility extends AceAbility {
@Override
public void onStart(Intent intent) {
super.onStart(intent);
// 注册
CalcInternalAbility.register(this);
...
}
@Override
public void onStop() {
// 去注册
CalcInternalAbility.deregister();
super.onStop();
}
}