學習服務卡片事件能力
簡要介紹
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刷新卡片功能就會用到這個。
使用這個功能有兩點必須要注意:
- 必須指定要拉起的UIAbility的launchType為singleton類型,否則無效。
- 必須添加權限 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。