1# 卡片拉起应用UIAbility到后台(call事件) 2 3 4许多应用希望借助卡片的能力,实现和应用在前台时相同的功能。例如音乐卡片,卡片上提供播放、暂停等按钮,点击不同按钮将触发音乐应用的不同功能,进而提高用户的体验。在卡片中使用[postCardAction](../reference/apis-arkui/js-apis-postCardAction.md#postcardaction-1)接口的call能力,能够将卡片提供方应用的指定的UIAbility拉到后台。同时,call能力提供了调用应用指定方法、传递数据的功能,使应用在后台运行时可以通过卡片上的按钮执行不同的功能。 5 6> **说明:** 7> 8> 本文主要介绍动态卡片的事件开发。对于静态卡片,请参见[FormLink](../reference/apis-arkui/arkui-ts/ts-container-formlink.md)。 9 10## 开发步骤 111. 创建动态卡片 12 13 新建一个名为WidgetEventCall的ArkTs动态卡片。 14 152. 页面布局代码实现 16 17 在卡片页面中布局两个按钮,点击按钮A或按钮B,会调用postCardAction向指定UIAbility发送call事件,在call事件内定义了需要调用的方法。按钮A和按钮B分别对应调用funA、funB方法,其中funA携带了formID参数,funB携带了formID和num参数,开发过程中请根据实际需要传参。postCardAction中的method参数为必填参数,用于标识需要调用的方法名称,与步骤3中UIAbility监听的方法一致,其他参数为非必填。 18 ```ts 19 //src/main/ets/widgeteventcallcard/pages/WidgetEventCall.ets 20 @Entry 21 @Component 22 struct WidgetEventCall { 23 @LocalStorageProp('formId') formId: string = '12400633174999288'; 24 private funA: string = '按钮A'; 25 private funB: string = '按钮B'; 26 27 build() { 28 RelativeContainer() { 29 Button(this.funA) 30 .id('funA__') 31 .fontSize($r('app.float.page_text_font_size')) 32 .fontWeight(FontWeight.Bold) 33 .alignRules({ 34 center: { anchor: '__container__', align: VerticalAlign.Center }, 35 middle: { anchor: '__container__', align: HorizontalAlign.Center } 36 }) 37 .onClick(() => { 38 postCardAction(this, { 39 action: 'call', 40 // 只能跳转到当前应用下的UIAbility,与module.json5中定义保持一致 41 abilityName: 'WidgetEventCallEntryAbility', 42 params: { 43 formId: this.formId, 44 // 需要调用的方法名称 45 method: 'funA' 46 } 47 }); 48 }) 49 Button(this.funB) 50 .id('funB__') 51 .fontSize($r('app.float.page_text_font_size')) 52 .fontWeight(FontWeight.Bold) 53 .margin({ top: 10 }) 54 .alignRules({ 55 top: { anchor: 'funA__', align: VerticalAlign.Bottom }, 56 middle: { anchor: '__container__', align: HorizontalAlign.Center } 57 }) 58 .onClick(() => { 59 postCardAction(this, { 60 action: 'call', 61 abilityName: 'WidgetEventCallEntryAbility', 62 params: { 63 formId: this.formId, 64 // 需要调用的方法名称 65 method: 'funB', 66 num: 1 67 } 68 }); 69 }) 70 } 71 .height('100%') 72 .width('100%') 73 } 74 } 75 ``` 763. 创建指定的UIAbility 77 78 在UIAbility中监听call事件,根据监听到的method参数中的方法名称调用对应方法,并通过[rpc.Parcelable](../reference/apis-ipc-kit/js-apis-rpc.md#parcelable9)获取参数。UIAbility中监听的方法与步骤2中调用的方法需保持一致。 79 ```ts 80 //src/main/ets/widgeteventcallcard/WidgetEventCallEntryAbility/WidgetEventCallEntryAbility.ets 81 import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit'; 82 import { BusinessError } from '@kit.BasicServicesKit'; 83 import { rpc } from '@kit.IPCKit'; 84 import { hilog } from '@kit.PerformanceAnalysisKit'; 85 86 const TAG: string = 'WidgetEventCallEntryAbility'; 87 const DOMAIN_NUMBER: number = 0xFF00; 88 const CONST_NUMBER_1: number = 1; 89 const CONST_NUMBER_2: number = 2; 90 91 // rpc通信返回类型的实现,用于rpc通信数据序列化和反序列化 92 class MyParcelable implements rpc.Parcelable { 93 num: number; 94 str: string; 95 96 constructor(num: number, str: string) { 97 this.num = num; 98 this.str = str; 99 } 100 101 marshalling(messageSequence: rpc.MessageSequence): boolean { 102 messageSequence.writeInt(this.num); 103 messageSequence.writeString(this.str); 104 return true; 105 } 106 107 unmarshalling(messageSequence: rpc.MessageSequence): boolean { 108 this.num = messageSequence.readInt(); 109 this.str = messageSequence.readString(); 110 return true; 111 } 112 } 113 114 export default class WidgetEventCallEntryAbility extends UIAbility { 115 // 如果UIAbility启动,在收到call事件后会触发onCreate生命周期回调 116 onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void { 117 try { 118 // 监听call事件所需的方法并调用 119 this.callee.on('funA', (data: rpc.MessageSequence) => { 120 // 获取call事件中传递的所有参数 121 hilog.info(DOMAIN_NUMBER, TAG, `FunACall param: ${JSON.stringify(data.readString())}`); 122 return new MyParcelable(CONST_NUMBER_1, 'aaa'); 123 }); 124 this.callee.on('funB', (data: rpc.MessageSequence) => { 125 // 获取call事件中传递的所有参数 126 hilog.info(DOMAIN_NUMBER, TAG, `FunBCall param: ${JSON.stringify(data.readString())}`); 127 return new MyParcelable(CONST_NUMBER_2, 'bbb'); 128 }); 129 } catch (err) { 130 hilog.error(DOMAIN_NUMBER, TAG, `Failed to register callee on. Cause: ${JSON.stringify(err as BusinessError)}`); 131 } 132 } 133 134 // 进程退出时,解除监听 135 onDestroy(): void | Promise<void> { 136 try { 137 this.callee.off('funA'); 138 this.callee.off('funB'); 139 } catch (err) { 140 hilog.error(DOMAIN_NUMBER, TAG, `Failed to register callee off. Cause: ${JSON.stringify(err as BusinessError)}`); 141 } 142 } 143 } 144 ``` 1454. 配置后台运行权限 146 147 call事件存在约束限制,卡片提供方应用需要在module.json5下添加后台运行权限([ohos.permission.KEEP_BACKGROUND_RUNNING](../security/AccessToken/permissions-for-all.md#ohospermissionkeep_background_running))。 148 ```ts 149 //src/main/module.json5 150 "requestPermissions":[ 151 { 152 "name": "ohos.permission.KEEP_BACKGROUND_RUNNING" 153 } 154 ] 155 ``` 1565. 配置指定的UIAbility 157 158 在module.json5的abilities数组内添加WidgetEventCallEntryAbility对应的配置信息。 159 ```ts 160 //src/main/module.json5 161 "abilities": [ 162 { 163 "name": 'WidgetEventCallEntryAbility', 164 "srcEntry": './ets/widgeteventcallcard/WidgetEventCallEntryAbility/WidgetEventCallEntryAbility.ets', 165 "description": '$string:WidgetEventCallCard_desc', 166 "icon": "$media:app_icon", 167 "label": "$string:WidgetEventCallCard_label", 168 "startWindowIcon": "$media:app_icon", 169 "startWindowBackground": "$color:start_window_background" 170 } 171 ] 172 ```