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 38**Index.ets示例代码如下:** 39```ts 40@Entry 41@Component 42struct Index { 43 @State message: string = 'UIExtension UserA'; 44 private myProxy: UIExtensionProxy | undefined = undefined; 45 46 build() { 47 Row() { 48 Column() { 49 Text(this.message) 50 .fontSize(30) 51 .size({ width: '100%', height: '50' }) 52 .fontWeight(FontWeight.Bold) 53 .textAlign(TextAlign.Center) 54 55 UIExtensionComponent( 56 { 57 bundleName: 'com.samples.uiextensionability', 58 abilityName: 'UIExtensionProvider', 59 moduleName: 'entry', 60 parameters: { 61 'ability.want.params.uiExtensionType': 'sys/commonUI', 62 } 63 }) 64 .onRemoteReady((proxy) => { 65 this.myProxy = proxy; 66 }) 67 .onReceive((data) => { 68 this.message = JSON.stringify(data); 69 }) 70 .onResult((data) => { 71 this.message = JSON.stringify(data); 72 }) 73 .onRelease((code) => { 74 this.message = "release code:" + code; 75 }) 76 .offset({ x: 0, y: 10 }) 77 .size({ width: 300, height: 300 }) 78 .border({ width: 5, color: 0x317AF7, radius: 10, style: BorderStyle.Dotted }) 79 80 UIExtensionComponent( 81 { 82 bundleName: 'com.samples.uiextension2', 83 abilityName: 'UIExtensionProviderB', 84 moduleName: 'entry', 85 parameters: { 86 'ability.want.params.uiExtensionType': 'sys/commonUI', 87 } 88 }) 89 .onRemoteReady((proxy) => { 90 this.myProxy = proxy; 91 }) 92 .onReceive((data) => { 93 this.message = JSON.stringify(data); 94 }) 95 .onResult((data) => { 96 this.message = JSON.stringify(data); 97 }) 98 .onRelease((code) => { 99 this.message = "release code:" + code; 100 }) 101 .offset({ x: 0, y: 50 }) 102 .size({ width: 300, height: 300 }) 103 .border({ width: 5, color: 0x317AF7, radius: 10, style: BorderStyle.Dotted }) 104 } 105 .width('100%') 106 } 107 .height('100%') 108 } 109} 110``` 111**图2** 根据上述代码,生成的Index页面如下: 112 113 114 115采用该进程模型,进程名格式为: 116process name [{bundleName}:{UIExtensionAbility的类型}] 117例如,process name [com.ohos.intentexecutedemo:xxx]。 118 119**图3** 进程模型展示 120 121 122 123### 同UIExtensionAbility类的所有UIExtensionAbility共进程 124根据[UIExtensionAbility](../reference/apis-ability-kit/js-apis-app-ability-uiExtensionAbility.md)类进行分配进程,拉起多个同样的UIExtensionAbility实例时,这些实例将配置在同一个进程中。将同UIExtensionAbility类配置在同一个进程下,方便应用针对UIExtensionAbility类型对实例进行管理。 125 126**图4** type模型配置示意图 127 128 129 130**Index.ets示例代码如下:** 131```ts 132@Entry 133@Component 134struct Index { 135 @State message: string = 'UIExtension User'; 136 private myProxy: UIExtensionProxy | undefined = undefined; 137 138 build() { 139 Row() { 140 Column() { 141 Text(this.message) 142 .fontSize(30) 143 .size({ width: '100%', height: '50' }) 144 .fontWeight(FontWeight.Bold) 145 .textAlign(TextAlign.Center) 146 147 UIExtensionComponent( 148 { 149 bundleName: 'com.samples.uiextensionability', 150 abilityName: 'UIExtensionProviderA', 151 moduleName: 'entry', 152 parameters: { 153 'ability.want.params.uiExtensionType': 'sys/commonUI', 154 } 155 }) 156 .onRemoteReady((proxy) => { 157 this.myProxy = proxy; 158 }) 159 .onReceive((data) => { 160 this.message = JSON.stringify(data); 161 }) 162 .onResult((data) => { 163 this.message = JSON.stringify(data); 164 }) 165 .onRelease((code) => { 166 this.message = "release code:" + code; 167 }) 168 .offset({ x: 0, y: 10 }) 169 .size({ width: 300, height: 300 }) 170 .border({ width: 5, color: 0x317AF7, radius: 10, style: BorderStyle.Dotted }) 171 172 UIExtensionComponent( 173 { 174 bundleName: 'com.samples.uiextensionability', 175 abilityName: 'UIExtensionProviderB', 176 moduleName: 'entry', 177 parameters: { 178 'ability.want.params.uiExtensionType': 'sys/commonUI', 179 } 180 }) 181 .onRemoteReady((proxy) => { 182 this.myProxy = proxy; 183 }) 184 .onReceive((data) => { 185 this.message = JSON.stringify(data); 186 }) 187 .onResult((data) => { 188 this.message = JSON.stringify(data); 189 }) 190 .onRelease((code) => { 191 this.message = "release code:" + code; 192 }) 193 .offset({ x: 0, y: 50 }) 194 .size({ width: 300, height: 300 }) 195 .border({ width: 5, color: 0x317AF7, radius: 10, style: BorderStyle.Dotted }) 196 } 197 .width('100%') 198 } 199 .height('100%') 200 } 201} 202``` 203**图5** 根据上述代码,生成的Index页面如下: 204 205 206 207采用该进程模型,进程名格式为: 208process name [{bundleName}:{UIExtensionAbility名}] 209例如,process name [com.ohos.intentexecutedemo:xxx]。 210 211**图6** 进程模型展示 212 213 214 215### UIExtensionAbility实例独立进程 216根据[UIExtensionAbility](../reference/apis-ability-kit/js-apis-app-ability-uiExtensionAbility.md)实例进行分配进程,配置了instance的UIExtensionAbility实例,将每个实例独立一个进程。独立进程的场景下,UIExtensionAbility实例之间只能通过跨进程进行通信,但实例之间的状态不会彼此影响,安全性更高。 217 218**图7** instance模型配置示意图 219 220 221 222 223**Index.ets示例代码如下:** 224```ts 225@Entry 226@Component 227struct Index { 228 @State message: string = 'UIExtension User' 229 private myProxy: UIExtensionProxy | undefined = undefined; 230 231 build() { 232 Row() { 233 Column() { 234 Text(this.message) 235 .fontSize(30) 236 .size({ width: '100%', height: '50' }) 237 .fontWeight(FontWeight.Bold) 238 .textAlign(TextAlign.Center) 239 240 UIExtensionComponent( 241 { 242 bundleName: 'com.samples.uiextensionability', 243 abilityName: 'UIExtensionProvider', 244 moduleName: 'entry', 245 parameters: { 246 'ability.want.params.uiExtensionType': 'sys/commonUI', 247 } 248 }) 249 .onRemoteReady((proxy) => { 250 this.myProxy = proxy; 251 }) 252 .onReceive((data) => { 253 this.message = JSON.stringify(data); 254 }) 255 .onResult((data) => { 256 this.message = JSON.stringify(data); 257 }) 258 .onRelease((code) => { 259 this.message = "release code:" + code; 260 }) 261 .offset({ x: 0, y: 10 }) 262 .size({ width: 300, height: 300 }) 263 .border({ width: 5, color: 0x317AF7, radius: 10, style: BorderStyle.Dotted }) 264 265 UIExtensionComponent( 266 { 267 bundleName: 'com.samples.uiextensionability', 268 abilityName: 'UIExtensionProvider', 269 moduleName: 'entry', 270 parameters: { 271 'ability.want.params.uiExtensionType': 'sys/commonUI', 272 } 273 }) 274 .onRemoteReady((proxy) => { 275 this.myProxy = proxy; 276 }) 277 .onReceive((data) => { 278 this.message = JSON.stringify(data); 279 }) 280 .onResult((data) => { 281 this.message = JSON.stringify(data); 282 }) 283 .onRelease((code) => { 284 this.message = "release code:" + code; 285 }) 286 .offset({ x: 0, y: 50 }) 287 .size({ width: 300, height: 300 }) 288 .border({ width: 5, color: 0x317AF7, radius: 10, style: BorderStyle.Dotted }) 289 } 290 .width('100%') 291 } 292 .height('100%') 293 } 294} 295``` 296**图8** 根据上述代码,生成的Index页面如下: 297 298 299 300采用该进程模型,进程名格式为: 301process name [{bundleName}:{UIExtensionAbility的类型}: {实例后缀}] 302例如,process name [com.ohos.intentexecutedemo:xxx:n]。 303 304**图9** 进程模型展示 305 306 307 308UIExtensionAbility通过[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)组件为使用方。 309 310## 开发步骤 311 312为了便于表述,本例中将提供[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)。 313 314### 开发UIExtensionAbility提供方 315 316开发者在实现一个[UIExtensionAbility](../reference/apis-ability-kit/js-apis-app-ability-uiExtensionAbility.md)提供方时,需要在DevEco Studio工程中手动新建一个UIExtensionAbility,具体步骤如下。 317 3181. 在工程Module对应的ets目录下,右键选择“New > Directory”,新建一个目录并命名为uiextensionability。 319 3202. 在uiextensionability目录,右键选择“New > File”,新建一个.ets文件并命名为UIExtensionAbility.ets。 321 3223. 打开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)生命周期回调。 323 324 ```ts 325 import { Want, UIExtensionAbility, UIExtensionContentSession } from '@kit.AbilityKit'; 326 327 const TAG: string = '[testTag] UIExtAbility'; 328 329 export default class UIExtAbility extends UIExtensionAbility { 330 onCreate() { 331 console.log(TAG, `onCreate`); 332 } 333 334 onForeground() { 335 console.log(TAG, `onForeground`); 336 } 337 338 onBackground() { 339 console.log(TAG, `onBackground`); 340 } 341 342 onDestroy() { 343 console.log(TAG, `onDestroy`); 344 } 345 346 onSessionCreate(want: Want, session: UIExtensionContentSession) { 347 console.log(TAG, `onSessionCreate, want: ${JSON.stringify(want)}}`); 348 let storage: LocalStorage = new LocalStorage(); 349 storage.setOrCreate('session', session); 350 session.loadContent('pages/Extension', storage); 351 } 352 353 onSessionDestroy(session: UIExtensionContentSession) { 354 console.log(TAG, `onSessionDestroy`); 355 } 356 } 357 ``` 358 3594. [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内容如下。 360 361 ```ts 362 import { UIExtensionContentSession } from '@kit.AbilityKit'; 363 364 let storage = LocalStorage.GetShared(); 365 const TAG: string = `[testTag] ExtensionPage`; 366 367 @Entry(storage) 368 @Component 369 struct Extension { 370 @State message: string = `UIExtension provider`; 371 private session: UIExtensionContentSession | undefined = storage.get<UIExtensionContentSession>('session'); 372 373 onPageShow() { 374 console.info(TAG, 'show'); 375 } 376 377 build() { 378 Row() { 379 Column() { 380 Text(this.message) 381 .fontSize(30) 382 .fontWeight(FontWeight.Bold) 383 .textAlign(TextAlign.Center) 384 385 Button("send data") 386 .width('80%') 387 .type(ButtonType.Capsule) 388 .margin({ top: 20 }) 389 .onClick(() => { 390 this.session?.sendData({ "data": 543321 }); 391 }) 392 393 Button("terminate self") 394 .width('80%') 395 .type(ButtonType.Capsule) 396 .margin({ top: 20 }) 397 .onClick(() => { 398 this.session?.terminateSelf(); 399 storage.clear(); 400 }) 401 402 Button("terminate self with result") 403 .width('80%') 404 .type(ButtonType.Capsule) 405 .margin({ top: 20 }) 406 .onClick(() => { 407 this.session?.terminateSelfWithResult({ 408 resultCode: 0, 409 want: { 410 bundleName: "com.example.uiextensiondemo", 411 parameters: { "result": 123456 } 412 } 413 }) 414 }) 415 } 416 } 417 .height('100%') 418 } 419 } 420 ``` 421 4225. 在工程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"为例。 423 424 ```json 425 { 426 "module": { 427 "extensionAbilities": [ 428 { 429 "name": "UIExtensionProvider", 430 "srcEntry": "./ets/uiextensionability/UIExtensionAbility.ets", 431 "description": "UIExtensionAbility", 432 "type": "sys/commonUI", 433 "exported": true, 434 "extensionProcessMode": "bundle" 435 }, 436 ] 437 } 438 } 439 ``` 440## 开发UIExtensionAbility使用方 441 442开发者可以在[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)。 443如在首页文件:pages/Index.ets中添加如下内容。 444 445```ts 446@Entry 447@Component 448struct Index { 449 @State message: string = 'UIExtension User'; 450 private myProxy: UIExtensionProxy | undefined = undefined; 451 452 build() { 453 Row() { 454 Column() { 455 Text(this.message) 456 .fontSize(30) 457 .size({ width: '100%', height: '50' }) 458 .fontWeight(FontWeight.Bold) 459 .textAlign(TextAlign.Center) 460 461 UIExtensionComponent( 462 { 463 bundleName: 'com.example.uiextensiondemo', 464 abilityName: 'UIExtensionProvider', 465 moduleName: 'entry', 466 parameters: { 467 'ability.want.params.uiExtensionType': 'sys/commonUI', 468 } 469 }) 470 .onRemoteReady((proxy) => { 471 this.myProxy = proxy; 472 }) 473 .onReceive((data) => { 474 this.message = JSON.stringify(data); 475 }) 476 .onResult((data) => { 477 this.message = JSON.stringify(data); 478 }) 479 .onRelease((code) => { 480 this.message = "release code:" + code; 481 }) 482 .offset({ x: 0, y: 30 }) 483 .size({ width: 300, height: 300 }) 484 .border({ width: 5, color: 0x317AF7, radius: 10, style: BorderStyle.Dotted }) 485 486 Button("sendData") 487 .type(ButtonType.Capsule) 488 .offset({ x: 0, y: 60 }) 489 .width('80%') 490 .type(ButtonType.Capsule) 491 .margin({ 492 top: 20 493 }) 494 .onClick(() => { 495 this.myProxy?.send({ 496 "data": 123456, 497 "message": "data from component" 498 }) 499 }) 500 } 501 .width('100%') 502 } 503 .height('100%') 504 } 505} 506```