#2023盲盒+码# 学习服务卡片事件能力 原创 精华
【本文正在参加 2023「盲盒」+码有奖征文活动】,活动链接 https://ost.51cto.com/posts/25284
简要介绍
ArkTS卡片内部和提供方应用间的交互,可以通过在卡片一端调用postCardAction来实现,当前支持router、message和call三种类型的事件。我们根据实际应用分别从五个方面简单学习一下:
- 服务卡片通过router跳转到应用指定页面;
- 服务卡片通过call调用应用页面在后台执行;
- 应用通过message事件刷新服务卡片内容;
- 应用通过router事件刷新服务卡片内容;
- 应用通过call事件刷新服务卡片内容;
为了充分理解和使用这五种能力,我计划新创建一个服务卡片,在上面放置5个按钮分别实现对应的功能,同时放置一个文本组件,用于显示事件刷新的结果。
- 按钮“主页面”,点击后直接跳转到主页面;
- 按钮“子页面”,点击后直接跳转到子页面;
- 按钮“message+1”,点击后通过message方式刷新服务卡片,并使文本数值加一。
- 按钮“call+2”,点击后通过call方式刷新服务卡片,并使文本数值加二。
- 按钮“router+3”,点击后通过router方式刷新服务卡片,并使文本数值加三。
效果预览
详细介绍
1.跳转到页面
应用中一共有两个页面Index.ets与Second.ets,点击卡片上相应的按钮跳转到对应界面,主要是通过调用postCardAction向指定UIAbility发送router事件,并在事件内定义需要传递的内容targetPage,该参数用来标识要打开的page页面。
Button('主页面', { type: ButtonType.Normal, stateEffect: true })
.borderRadius(8)
.margin(2)
.onClick(() => {
postCardAction(this, {
'action': 'router',
'abilityName': 'EntryAbility',
'params': {
'targetPage': 'index'
}
});
})
Button('子页面', { type: ButtonType.Normal, stateEffect: true })
.borderRadius(8)
.margin(1)
.onClick(() => {
postCardAction(this, {
'action': 'router',
'abilityName': 'EntryAbility',
'params': {
'targetPage': 'second'
}
});
})
在EntryAbility.ts中接收router事件并获取参数,根据传递的params不同,设置参数selectPage,在onWindowStageCreate函数中,根据传入的不同值,通过windowStage.loadContent拉起不同的页面。
onCreate(want, launchParam) {
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate');
if (want.parameters.params !== undefined) {
let params = JSON.parse(want.parameters.params);
console.info("onCreate router targetPage:" + params.targetPage);
selectPage = params.targetPage;
}
}
onNewWant(want, launchParam) {
console.info("onNewWant want:" + JSON.stringify(want));
if (want.parameters.params !== undefined) {
let params = JSON.parse(want.parameters.params);
console.info("onNewWant router targetPage:" + params.targetPage);
selectPage = params.targetPage;
}
if (currentWindowStage != null) {
this.onWindowStageCreate(currentWindowStage);
}
}
onWindowStageCreate(windowStage: window.WindowStage) {
// Main window is created, set main page for this ability
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate');
let targetPage;
// 根据传递的targetPage不同,选择拉起不同的页面
switch (selectPage) {
case 'index':
targetPage = 'pages/Index';
break;
case 'second':
targetPage = 'pages/Second';
break;
default:
targetPage = 'pages/Index';
}
if (currentWindowStage === null) {
currentWindowStage = windowStage;
}
windowStage.loadContent(targetPage, (err, data) => {
if (err.code) {
hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
return;
}
hilog.info(0x0000, 'testTag', 'Succeeded in loading the content. Data: %{public}s', JSON.stringify(data) ?? '');
});
}
2.调用应用到后台
在卡片中使用postCardAction接口的call能力,能够将卡片提供方应用的指定UIAbility拉到后台。同时,call能力提供了调用应用指定方法、传递数据的功能,使应用在后台运行时可以通过卡片上的按钮执行不同的功能。
我们在这个示例中,通过postCardAction的call方式,调用EntryAbility中的funA方法,在funA方法具体实现函数funACall中,对传递的值进行加二操作。在EntryAbility中,funA方法也要进行注册和注销。
卡片中调用postCardAction方法时,action参数应指定为call,同时参数表中必须带method参数。
Button('call+2', { type: ButtonType.Normal, stateEffect: true })
.borderRadius(8)
.margin(1)
.onClick(() => {
console.info('postCardAction to EntryAbility');
postCardAction(this, {
'action': 'call',
'abilityName': 'EntryAbility', // 只能跳转到当前应用下的UIAbility
'params': {
'method': 'funA',
'formId': this.formId,
'detail': this.detail
}
});
})
UIAbility需要onCreate生命周期中打开监听所需的方法,onDestroy生命周期中关闭监听所需的方法。
const MSG_SEND_METHOD: string = 'funA'
onCreate(want, launchParam) {
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate');
try {
// 监听call事件所需的方法
this.callee.on(MSG_SEND_METHOD, FunACall);
} catch (error) {
console.log(`${MSG_SEND_METHOD} register failed with error ${JSON.stringify(error)}`)
}
}
onDestroy() {
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onDestroy');
try {
this.callee.off(MSG_SEND_METHOD);
} catch (error) {
console.log(`${MSG_SEND_METHOD} register failed with error ${JSON.stringify(error)}`)
}
}
在打开监听时,需要指定具体的实现函数。
function FunACall(data) {
// 获取call事件中传递的所有参数
let params = JSON.parse(data.readString())
if (params.formId !== undefined) {
let curFormId = params.formId;
let message = params.detail;
console.info(`UpdateForm formId: ${curFormId}, message: ${message}`);
let formData = {
"detail": (Number.parseInt(message) + 2).toString()
};
let formMsg = formBindingData.createFormBindingData(formData)
formProvider.updateForm(curFormId, formMsg).then((data) => {
console.info('updateForm success.' + JSON.stringify(data));
}).catch((error) => {
console.error('updateForm failed:' + JSON.stringify(error));
})
}
return null;
}
这个功能通过call方式拉起指定UIAbility到后台,隐式的执行计算操作,后面的通过call刷新卡片功能就会用到这个。
==使用这个功能有两点必须要注意:
1.必须指定要拉起的UIAbility的launchType为singleton类型,否则无效;
2.必须添加权限 ohos.permission.KEEP_BACKGROUND_RUNNING,否则也无效;==
3.通过message刷新卡片
卡片页面中可以通过postCardAction接口触发message事件拉起FormExtensionAbility。
示例中,我们通过message的方式更新传递的值,并执行加一操作。
卡片中实现,
Button('message+1', { type: ButtonType.Normal, stateEffect: true })
.borderRadius(8)
.margin(1)
.onClick(() => {
console.info('postCardAction to EntryAbility');
postCardAction(this, {
'action': 'message',
'params': {
'detail': this.detail
}
});
})
在FormExtensionAbility的onFormEvent生命周期中调用updateForm接口刷新卡片。
onFormEvent(formId, message) {
// Called when a specified message event defined by the form provider is triggered.
console.info(`FormAbility onEvent, formId = ${formId}, message: ${JSON.stringify(message)}`);
let params = JSON.parse(message);
let detail = '0'
if (params.detail !== undefined) {
detail = params.detail;
}
let formData = {
'detail': (Number.parseInt(detail) + 1).toString()
};
let formInfo = formBindingData.createFormBindingData(formData)
formProvider.updateForm(formId, formInfo).then((data) => {
console.info('FormAbility updateForm success.' + JSON.stringify(data));
}).catch((error) => {
console.error('FormAbility updateForm failed: ' + JSON.stringify(error));
})
}
4.通过router刷新卡片
卡片页面中可以通过postCardAction接口触发router事件拉起UIAbility,然后由UIAbility刷新卡片内容,此时会把page页面调用到前台。
示例中也是传递当前文本参数,然后打开新的页面,同时会更新传递的值并加三操作,然后再formProvider.updateForm反馈给卡片页面。
卡片中的实现:
Button('router+3', { type: ButtonType.Normal, stateEffect: true })
.borderRadius(8)
.margin(2)
.onClick(() => {
console.info('postCardAction to EntryAbility');
postCardAction(this, {
'action': 'router',
'abilityName': 'EntryAbility',
'params': {
'detail': this.detail
}
});
})
在UIAbility的onCreate()或者onNewWant()生命周期中可以通过入参want获取传递过来的参数信息,然后调用updateForm接口刷新卡片。
onCreate(want, launchParam) {
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate');
if (want.parameters[formInfo.FormParam.IDENTITY_KEY] !== undefined) {
let curFormId = want.parameters[formInfo.FormParam.IDENTITY_KEY];
// let detail = JSON.parse(want.parameters.params).detail;
let detail = JSON.parse(want.parameters.params).detail;
if (detail !== undefined) {
console.info(`UpdateForm formId: ${curFormId}, message: ${detail}`);
let formData = {
"detail": (Number.parseInt(detail) + 3).toString()
};
let formMsg = formBindingData.createFormBindingData(formData)
formProvider.updateForm(curFormId, formMsg).then((data) => {
console.info('updateForm success.' + JSON.stringify(data));
}).catch((error) => {
console.error('updateForm failed:' + JSON.stringify(error));
})
}
}
}
onNewWant(want, launchParam) {
console.info("onNewWant want:" + JSON.stringify(want));
if (want.parameters[formInfo.FormParam.IDENTITY_KEY] !== undefined) {
let curFormId = want.parameters[formInfo.FormParam.IDENTITY_KEY];
let detail = JSON.parse(want.parameters.params).detail;
if (detail !== undefined) {
console.info(`UpdateForm formId: ${curFormId}, message: ${detail}`);
let formData = {
"detail": (Number.parseInt(detail) + 3).toString()
};
let formMsg = formBindingData.createFormBindingData(formData)
formProvider.updateForm(curFormId, formMsg).then((data) => {
console.info('updateForm success.' + JSON.stringify(data));
}).catch((error) => {
console.error('updateForm failed:' + JSON.stringify(error));
})
}
}
}
5.通过call刷新卡片
通过call刷新卡片和之前的调用应用到后台方法类似,在调用后台的基础上加入了更新卡片的功能。
在使用postCardAction接口的call事件时,需要在FormExtensionAbility中的onAddForm生命周期回调中更新formId。
export default class EntryFormAbility extends FormExtensionAbility {
onAddForm(want) {
// Called to return a FormBindingData object.
// let formData = {};
// return formBindingData.createFormBindingData(formData);
// let formId = want.parameters["ohos.extra.param.key.form_identity"];
// let dataObj1 = {
// "formId": formId
// };
// let obj1 = formBindingData.createFormBindingData(dataObj1);
// return obj1;
let formId = want.parameters["ohos.extra.param.key.form_identity"];
let formData = {
"formId": formId
};
return formBindingData.createFormBindingData(formData);
}
}
服务卡片中的按钮就是之前的拉起后台的按钮,功能一样
Button('call+2', { type: ButtonType.Normal, stateEffect: true })
.borderRadius(8)
.margin(1)
.onClick(() => {
console.info('postCardAction to EntryAbility');
postCardAction(this, {
'action': 'call',
'abilityName': 'EntryAbility', // 只能跳转到当前应用下的UIAbility
'params': {
'method': 'funA',
'formId': this.formId,
'detail': this.detail
}
});
})
具体实现是在UIAbility的onCreate生命周期中监听call事件回调的方法funACall,对传递进来的值加二更新,然后调用updateForm接口刷新卡片。
function FunACall(data) {
// 获取call事件中传递的所有参数
let params = JSON.parse(data.readString())
if (params.formId !== undefined) {
let curFormId = params.formId;
let message = params.detail;
console.info(`UpdateForm formId: ${curFormId}, message: ${message}`);
let formData = {
"detail": (Number.parseInt(message) + 2).toString()
};
let formMsg = formBindingData.createFormBindingData(formData)
formProvider.updateForm(curFormId, formMsg).then((data) => {
console.info('updateForm success.' + JSON.stringify(data));
}).catch((error) => {
console.error('updateForm failed:' + JSON.stringify(error));
})
}
return null;
}
==和之前一样,要注意两点,指定要拉起的UIAbility的launchType为singleton类型,添加权限 ohos.permission.KEEP_BACKGROUND_RUNNING,否则无效;==
学习总结
服务卡片和应用之间的消息传递是经常要用的功能,服务卡片的数据更新也是必不可少的,鸿蒙一共提供了三种方式,我们可以根据需要选择合适的去使用,message方式是与FormExtensionAbility打交道,router和call是与UIAbility打交道。支持call方式时一定不要忘了,在module.json5中设置launchType和ohos.permission.KEEP_BACKGROUND_RUNNING。
通过这5个事件已经涵盖大部分功能了