1# UIExtensionAbility 2 3## 概述 4 5[UIExtensionAbility](../reference/apis-ability-kit/js-apis-app-ability-uiExtensionAbility.md)是UI类型的ExtensionAbility,常用于有进程隔离诉求的系统弹窗、状态栏、胶囊等模块化开发的场景。有嵌入式显示与系统弹窗两种形式。 6- 嵌入式显示启动需要与[UIExtensionComponent](../reference/apis-arkui/arkui-ts/ts-container-ui-extension-component-sys.md)一起配合使用,开发者可以在UIAbility的页面中通过UIExtensionComponent嵌入提供方应用的UIExtensionAbility提供的UI。UIExtensionAbility会在独立于UIAbility的进程中运行,完成其页面的布局和渲染。 7- 系统弹窗启动形式需要调用指定接口[requestModalUIExtensionAbility](../reference/apis-ability-kit/js-apis-inner-application-serviceExtensionContext-sys.md#serviceextensioncontextrequestmodaluiextension11)或调用应用封装的指定接口启动UIExtensionAbility。 8 9## 约束限制 10- 当前"sys/commonUI"、"sysDialog"和"sysPicker"类型的UIExtensionAbility仅支持系统应用使用,更详细的UIExtensionAbility类型介绍及对应权限管控可参见:[module.json5配置文件](../quick-start/module-configuration-file.md)。 11- UIExtensionAbility仅支持拥有前台窗口的应用拉起,处于后台运行的应用无法拉起UIExtensionAbility。 12 13## 生命周期 14[UIExtensionAbility](../reference/apis-ability-kit/js-apis-app-ability-uiExtensionAbility.md)提供了[onCreate](../reference/apis-ability-kit/js-apis-app-ability-uiExtensionAbility.md#uiextensionabilityoncreate)、[onSessionCreate](../reference/apis-ability-kit/js-apis-app-ability-uiExtensionAbility.md#uiextensionabilityonsessioncreate)、[onSessionDestroy](../reference/apis-ability-kit/js-apis-app-ability-uiExtensionAbility.md#uiextensionabilityonsessiondestroy)、[onForeground](../reference/apis-ability-kit/js-apis-app-ability-uiExtensionAbility.md#uiextensionabilityonforeground)、[onBackground](../reference/apis-ability-kit/js-apis-app-ability-uiExtensionAbility.md#uiextensionabilityonbackground)和[onDestroy](../reference/apis-ability-kit/js-apis-app-ability-uiExtensionAbility.md#uiextensionabilityondestroy)生命周期回调,根据需要重写对应的回调方法。 15 16- **onCreate**:当UIExtensionAbility创建时回调,执行初始化业务逻辑操作。 17- **onSessionCreate**:当UIExtensionAbility界面内容对象创建后调用。 18- **onSessionDestroy**:当UIExtensionAbility界面内容对象销毁后调用。 19- **onForeground**:当UIExtensionAbility从后台转到前台时触发。 20- **onBackground**:当UIExtensionAbility从前台转到后台时触发。 21- **onDestroy**:当UIExtensionAbility销毁时回调,可以执行资源清理等操作。 22 23## 选择合适的UIExtensionAbility进程模型 24[UIExtensionAbility](../reference/apis-ability-kit/js-apis-app-ability-uiExtensionAbility.md)支持多实例,每个嵌入式显示对应一个UIExtensionAbility实例。多实例场景下默认是多进程,可配置多进程模型。 25当应用中存在多个UIExtensionAbility实例,这些实例可以为多个独立进程,也可以共用同一个进程,还可以分为多组、同组实例共用同一个进程。通过[module.json5](../quick-start/module-configuration-file.md)配置文件中的extensionProcessMode字段,即可为选择对应的进程模型,三种模型对比如下: 26| 进程模型 | extensionProcessMode字段配置 | 说明 | 27| --------| --------| --------| 28| 同一bundle中所有UIExtensionAbility共进程 |bundle| UIExtensionAbility实例之间的通信无需跨IPC通信;实例之间的状态不独立,会存在相互影响。| 29| 相同name的UIExtensionAbility共进程 | type |将同UIExtensionAbility类配置在同一个进程下,便于应用针对UIExtensionAbility类型对实例进行管理。| 30| 每个UIExtensionAbility为独立进程 | instance | UIExtensionAbility实例之间的状态不会彼此影响,安全性更高;实例之间只能通过跨进程进行通信。 | 31### Bundle中的所有UIExtensionAbility共进程 32同一个bundle下的[UIExtensionAbility](../reference/apis-ability-kit/js-apis-app-ability-uiExtensionAbility.md)配置在同一个进程中,便于多实例间的通信。需要关注的是,各个实例之间的状态会彼此影响,当进程中的一个实例异常退出,将导致进程中所有的实例也都会退出; 33 34**图1** bundle模型配置示意图 35 36 37**Index.ets示例代码如下:** 38```ts 39@Entry 40@Component 41struct Index { 42 @State message: string = 'UIExtension UserA'; 43 private myProxy: UIExtensionProxy | undefined = undefined; 44 45 build() { 46 Row() { 47 Column() { 48 Text(this.message) 49 .fontSize(30) 50 .size({ width: '100%', height: '50' }) 51 .fontWeight(FontWeight.Bold) 52 .textAlign(TextAlign.Center) 53 54 UIExtensionComponent( 55 { 56 bundleName: 'com.samples.uiextensionability', 57 abilityName: 'UIExtensionProvider', 58 moduleName: 'entry', 59 parameters: { 60 'ability.want.params.uiExtensionType': 'sys/commonUI', 61 } 62 }) 63 .onRemoteReady((proxy) => { 64 this.myProxy = proxy; 65 }) 66 .onReceive((data) => { 67 this.message = JSON.stringify(data); 68 }) 69 .onResult((data) => { 70 this.message = JSON.stringify(data); 71 }) 72 .onRelease((code) => { 73 this.message = "release code:" + code; 74 }) 75 .offset({ x: 0, y: 10 }) 76 .size({ width: 300, height: 300 }) 77 .border({ width: 5, color: 0x317AF7, radius: 10, style: BorderStyle.Dotted }) 78 79 UIExtensionComponent( 80 { 81 bundleName: 'com.samples.uiextension2', 82 abilityName: 'UIExtensionProviderB', 83 moduleName: 'entry', 84 parameters: { 85 'ability.want.params.uiExtensionType': 'sys/commonUI', 86 } 87 }) 88 .onRemoteReady((proxy) => { 89 this.myProxy = proxy; 90 }) 91 .onReceive((data) => { 92 this.message = JSON.stringify(data); 93 }) 94 .onResult((data) => { 95 this.message = JSON.stringify(data); 96 }) 97 .onRelease((code) => { 98 this.message = "release code:" + code; 99 }) 100 .offset({ x: 0, y: 50 }) 101 .size({ width: 300, height: 300 }) 102 .border({ width: 5, color: 0x317AF7, radius: 10, style: BorderStyle.Dotted }) 103 } 104 .width('100%') 105 } 106 .height('100%') 107 } 108} 109``` 110**图2** 根据上述代码,生成的Index页面如下: 111 112 113采用该进程模型,进程名格式为: 114process name [{bundleName}:{UIExtensionAbility的类型}] 115例如,process name [com.ohos.intentexecutedemo:xxx]。 116**图3** 进程模型展示 117 118 119### 同UIExtensionAbility类的所有UIExtensionAbility共进程 120根据[UIExtensionAbility](../reference/apis-ability-kit/js-apis-app-ability-uiExtensionAbility.md)类进行分配进程,拉起多个同样的UIExtensionAbility实例时,这些实例将配置在同一个进程中。将同UIExtensionAbility类配置在同一个进程下,方便应用针对UIExtensionAbility类型对实例进行管理; 121 122**图4** type模型配置示意图 123 124 125**Index.ets示例代码如下:** 126```ts 127@Entry 128@Component 129struct Index { 130 @State message: string = 'UIExtension User'; 131 private myProxy: UIExtensionProxy | undefined = undefined; 132 133 build() { 134 Row() { 135 Column() { 136 Text(this.message) 137 .fontSize(30) 138 .size({ width: '100%', height: '50' }) 139 .fontWeight(FontWeight.Bold) 140 .textAlign(TextAlign.Center) 141 142 UIExtensionComponent( 143 { 144 bundleName: 'com.samples.uiextensionability', 145 abilityName: 'UIExtensionProviderA', 146 moduleName: 'entry', 147 parameters: { 148 'ability.want.params.uiExtensionType': 'sys/commonUI', 149 } 150 }) 151 .onRemoteReady((proxy) => { 152 this.myProxy = proxy; 153 }) 154 .onReceive((data) => { 155 this.message = JSON.stringify(data); 156 }) 157 .onResult((data) => { 158 this.message = JSON.stringify(data); 159 }) 160 .onRelease((code) => { 161 this.message = "release code:" + code; 162 }) 163 .offset({ x: 0, y: 10 }) 164 .size({ width: 300, height: 300 }) 165 .border({ width: 5, color: 0x317AF7, radius: 10, style: BorderStyle.Dotted }) 166 167 UIExtensionComponent( 168 { 169 bundleName: 'com.samples.uiextensionability', 170 abilityName: 'UIExtensionProviderB', 171 moduleName: 'entry', 172 parameters: { 173 'ability.want.params.uiExtensionType': 'sys/commonUI', 174 } 175 }) 176 .onRemoteReady((proxy) => { 177 this.myProxy = proxy; 178 }) 179 .onReceive((data) => { 180 this.message = JSON.stringify(data); 181 }) 182 .onResult((data) => { 183 this.message = JSON.stringify(data); 184 }) 185 .onRelease((code) => { 186 this.message = "release code:" + code; 187 }) 188 .offset({ x: 0, y: 50 }) 189 .size({ width: 300, height: 300 }) 190 .border({ width: 5, color: 0x317AF7, radius: 10, style: BorderStyle.Dotted }) 191 } 192 .width('100%') 193 } 194 .height('100%') 195 } 196} 197``` 198**图5** 根据上述代码,生成的Index页面如下: 199 200 201采用该进程模型,进程名格式为: 202process name [{bundleName}:{UIExtensionAbility名}] 203例如,process name [com.ohos.intentexecutedemo:xxx]。 204**图6** 进程模型展示 205 206 207### UIExtensionAbility实例独立进程 208根据[UIExtensionAbility](../reference/apis-ability-kit/js-apis-app-ability-uiExtensionAbility.md)实例进行分配进程,配置了instance的UIExtensionAbility实例,将每个实例独立一个进程。独立进程的场景下,UIExtensionAbility实例之间只能通过跨进程进行通信,但实例之间的状态不会彼此影响,安全性更高; 209 210**图7** instance模型配置示意图 211 212 213 214**Index.ets示例代码如下:** 215```ts 216@Entry 217@Component 218struct Index { 219 @State message: string = 'UIExtension User' 220 private myProxy: UIExtensionProxy | undefined = undefined; 221 222 build() { 223 Row() { 224 Column() { 225 Text(this.message) 226 .fontSize(30) 227 .size({ width: '100%', height: '50' }) 228 .fontWeight(FontWeight.Bold) 229 .textAlign(TextAlign.Center) 230 231 UIExtensionComponent( 232 { 233 bundleName: 'com.samples.uiextensionability', 234 abilityName: 'UIExtensionProvider', 235 moduleName: 'entry', 236 parameters: { 237 'ability.want.params.uiExtensionType': 'sys/commonUI', 238 } 239 }) 240 .onRemoteReady((proxy) => { 241 this.myProxy = proxy; 242 }) 243 .onReceive((data) => { 244 this.message = JSON.stringify(data); 245 }) 246 .onResult((data) => { 247 this.message = JSON.stringify(data); 248 }) 249 .onRelease((code) => { 250 this.message = "release code:" + code; 251 }) 252 .offset({ x: 0, y: 10 }) 253 .size({ width: 300, height: 300 }) 254 .border({ width: 5, color: 0x317AF7, radius: 10, style: BorderStyle.Dotted }) 255 256 UIExtensionComponent( 257 { 258 bundleName: 'com.samples.uiextensionability', 259 abilityName: 'UIExtensionProvider', 260 moduleName: 'entry', 261 parameters: { 262 'ability.want.params.uiExtensionType': 'sys/commonUI', 263 } 264 }) 265 .onRemoteReady((proxy) => { 266 this.myProxy = proxy; 267 }) 268 .onReceive((data) => { 269 this.message = JSON.stringify(data); 270 }) 271 .onResult((data) => { 272 this.message = JSON.stringify(data); 273 }) 274 .onRelease((code) => { 275 this.message = "release code:" + code; 276 }) 277 .offset({ x: 0, y: 50 }) 278 .size({ width: 300, height: 300 }) 279 .border({ width: 5, color: 0x317AF7, radius: 10, style: BorderStyle.Dotted }) 280 } 281 .width('100%') 282 } 283 .height('100%') 284 } 285} 286``` 287**图8** 根据上述代码,生成的Index页面如下: 288 289 290采用该进程模型,进程名格式为: 291process name [{bundleName}:{UIExtensionAbility的类型}: {实例后缀}] 292例如,process name [com.ohos.intentexecutedemo:xxx:n]。 293**图9** 进程模型展示 294 295 296UIExtensionAbility通过[UIExtensionContext](../reference/apis-ability-kit/js-apis-inner-application-uiExtensionContext.md)和[UIExtensionContentSession](../reference/apis-ability-kit/js-apis-app-ability-uiExtensionContentSession.md)提供相关能力。本文描述中称被启动的UIExtensionAbility为提供方,称启动UIExtensionAbility的[UIExtensionComponent](../reference/apis-arkui/arkui-ts/ts-container-ui-extension-component-sys.md)组件为使用方。 297 298## 开发步骤 299 300为了便于表述,本例中将提供[UIExtensionAbility](../reference/apis-ability-kit/js-apis-app-ability-uiExtensionAbility.md)能力的一方称为提供方,将启动UIExtensionAbility的一方称为使用方,本例中使用方通过[UIExtensionComponent](../reference/apis-arkui/arkui-ts/ts-container-ui-extension-component-sys.md)容器启动UIExtensionAbility。系统弹框形式的使用方开发示例可参考文档:[requestModalUIExtension](../reference/apis-ability-kit/js-apis-inner-application-serviceExtensionContext-sys.md#serviceextensioncontextrequestmodaluiextension11)。 301 302### 开发UIExtensionAbility提供方 303 304开发者在实现一个[UIExtensionAbility](../reference/apis-ability-kit/js-apis-app-ability-uiExtensionAbility.md)提供方时,需要在DevEco Studio工程中手动新建一个UIExtensionAbility,具体步骤如下。 305 3061. 在工程Module对应的ets目录下,右键选择“New > Directory”,新建一个目录并命名为uiextensionability。 307 3082. 在uiextensionability目录,右键选择“New > File”,新建一个.ets文件并命名为UIExtensionAbility.ets。 309 3103. 打开UIExtensionAbility.ets,导入UIExtensionAbility的依赖包,自定义类继承UIExtensionAbility并实现[onCreate](../reference/apis-ability-kit/js-apis-app-ability-uiExtensionAbility.md#uiextensionabilityoncreate)、[onSessionCreate](../reference/apis-ability-kit/js-apis-app-ability-uiExtensionAbility.md#uiextensionabilityonsessioncreate)、[onSessionDestroy](../reference/apis-ability-kit/js-apis-app-ability-uiExtensionAbility.md#uiextensionabilityonsessiondestroy)、[onForeground](../reference/apis-ability-kit/js-apis-app-ability-uiExtensionAbility.md#uiextensionabilityonforeground)、[onBackground](../reference/apis-ability-kit/js-apis-app-ability-uiExtensionAbility.md#uiextensionabilityonbackground)和[onDestroy](../reference/apis-ability-kit/js-apis-app-ability-uiExtensionAbility.md#uiextensionabilityondestroy)生命周期回调。 311 312 ```ts 313 import { Want, UIExtensionAbility, UIExtensionContentSession } from '@kit.AbilityKit'; 314 315 const TAG: string = '[testTag] UIExtAbility'; 316 317 export default class UIExtAbility extends UIExtensionAbility { 318 onCreate() { 319 console.log(TAG, `onCreate`); 320 } 321 322 onForeground() { 323 console.log(TAG, `onForeground`); 324 } 325 326 onBackground() { 327 console.log(TAG, `onBackground`); 328 } 329 330 onDestroy() { 331 console.log(TAG, `onDestroy`); 332 } 333 334 onSessionCreate(want: Want, session: UIExtensionContentSession) { 335 console.log(TAG, `onSessionCreate, want: ${JSON.stringify(want)}}`); 336 let storage: LocalStorage = new LocalStorage(); 337 storage.setOrCreate('session', session); 338 session.loadContent('pages/Extension', storage); 339 } 340 341 onSessionDestroy(session: UIExtensionContentSession) { 342 console.log(TAG, `onSessionDestroy`); 343 } 344 } 345 ``` 346 3474. [UIExtensionAbility](../reference/apis-ability-kit/js-apis-app-ability-uiExtensionAbility.md)的[onSessionCreate](../reference/apis-ability-kit/js-apis-app-ability-uiExtensionAbility.md#uiextensionabilityonsessioncreate)中加载了入口页面文件pages/extension.ets, 并在entry\src\main\resources\base\profile\main_pages.json文件中添加"pages/Extension"声明,extension.ets内容如下: 348 349 ```ts 350 import { UIExtensionContentSession } from '@kit.AbilityKit'; 351 352 let storage = LocalStorage.GetShared(); 353 const TAG: string = `[testTag] ExtensionPage`; 354 355 @Entry(storage) 356 @Component 357 struct Extension { 358 @State message: string = `UIExtension provider`; 359 private session: UIExtensionContentSession | undefined = storage.get<UIExtensionContentSession>('session'); 360 361 onPageShow() { 362 console.info(TAG, 'show'); 363 } 364 365 build() { 366 Row() { 367 Column() { 368 Text(this.message) 369 .fontSize(30) 370 .fontWeight(FontWeight.Bold) 371 .textAlign(TextAlign.Center) 372 373 Button("send data") 374 .width('80%') 375 .type(ButtonType.Capsule) 376 .margin({ top: 20 }) 377 .onClick(() => { 378 this.session?.sendData({ "data": 543321 }); 379 }) 380 381 Button("terminate self") 382 .width('80%') 383 .type(ButtonType.Capsule) 384 .margin({ top: 20 }) 385 .onClick(() => { 386 this.session?.terminateSelf(); 387 storage.clear(); 388 }) 389 390 Button("terminate self with result") 391 .width('80%') 392 .type(ButtonType.Capsule) 393 .margin({ top: 20 }) 394 .onClick(() => { 395 this.session?.terminateSelfWithResult({ 396 resultCode: 0, 397 want: { 398 bundleName: "com.example.uiextensiondemo", 399 parameters: { "result": 123456 } 400 } 401 }) 402 }) 403 } 404 } 405 .height('100%') 406 } 407 } 408 ``` 409 4105. 在工程Module对应的[module.json5配置文件](../quick-start/module-configuration-file.md)中注册[UIExtensionAbility](../reference/apis-ability-kit/js-apis-app-ability-uiExtensionAbility.md),type标签需要设置为UIExtensionAbility中配置的对应类型,srcEntry标签表示当前UIExtensionAbility组件所对应的代码路径。extensionProcessMode标签标识多实例的进程模型,此处以"bundle"为例。 411 412 ```json 413 { 414 "module": { 415 "extensionAbilities": [ 416 { 417 "name": "UIExtensionProvider", 418 "srcEntry": "./ets/uiextensionability/UIExtensionAbility.ets", 419 "description": "UIExtensionAbility", 420 "type": "sys/commonUI", 421 "exported": true, 422 "extensionProcessMode": "bundle" 423 }, 424 ] 425 } 426 } 427 ``` 428## 开发UIExtensionAbility使用方 429 430开发者可以在[UIAbility](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md)的页面中通过[UIExtensionComponent](../reference/apis-arkui/arkui-ts/ts-container-ui-extension-component-sys.md)容器加载自己应用内的[UIExtensionAbility](../reference/apis-ability-kit/js-apis-app-ability-uiExtensionAbility.md)。 431如在首页文件:pages/Index.ets中添加如下内容: 432 433```ts 434@Entry 435@Component 436struct Index { 437 @State message: string = 'UIExtension User'; 438 private myProxy: UIExtensionProxy | undefined = undefined; 439 440 build() { 441 Row() { 442 Column() { 443 Text(this.message) 444 .fontSize(30) 445 .size({ width: '100%', height: '50' }) 446 .fontWeight(FontWeight.Bold) 447 .textAlign(TextAlign.Center) 448 449 UIExtensionComponent( 450 { 451 bundleName: 'com.example.uiextensiondemo', 452 abilityName: 'UIExtensionProvider', 453 moduleName: 'entry', 454 parameters: { 455 'ability.want.params.uiExtensionType': 'sys/commonUI', 456 } 457 }) 458 .onRemoteReady((proxy) => { 459 this.myProxy = proxy; 460 }) 461 .onReceive((data) => { 462 this.message = JSON.stringify(data); 463 }) 464 .onResult((data) => { 465 this.message = JSON.stringify(data); 466 }) 467 .onRelease((code) => { 468 this.message = "release code:" + code; 469 }) 470 .offset({ x: 0, y: 30 }) 471 .size({ width: 300, height: 300 }) 472 .border({ width: 5, color: 0x317AF7, radius: 10, style: BorderStyle.Dotted }) 473 474 Button("sendData") 475 .type(ButtonType.Capsule) 476 .offset({ x: 0, y: 60 }) 477 .width('80%') 478 .type(ButtonType.Capsule) 479 .margin({ 480 top: 20 481 }) 482 .onClick(() => { 483 this.myProxy?.send({ 484 "data": 123456, 485 "message": "data from component" 486 }) 487 }) 488 } 489 .width('100%') 490 } 491 .height('100%') 492 } 493} 494```