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![uiextability-bundle-processmodel](figures/uiextability-bundle-processmodel.png)
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![uiextension-bundle-example](figures/uiextability-bundle-example.png)
114
115采用该进程模型,进程名格式为:
116process name [{bundleName}:{UIExtensionAbility的类型}]
117例如,process name [com.ohos.intentexecutedemo:xxx]。
118
119**图3** 进程模型展示
120
121![uiextension-bundle-process-example](figures/uiextability-bundle-process-example.png)
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![uiextability-type-processmodel](figures/uiextability-type-processmodel.png)
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![uiextability-type-example](figures/uiextability-type-example.png)
206
207采用该进程模型,进程名格式为:
208process name [{bundleName}:{UIExtensionAbility名}]
209例如,process name [com.ohos.intentexecutedemo:xxx]。
210
211**图6** 进程模型展示
212
213![uiextability-type-process-example](figures/uiexteability-type-precess-example.png)
214
215### UIExtensionAbility实例独立进程
216根据[UIExtensionAbility](../reference/apis-ability-kit/js-apis-app-ability-uiExtensionAbility.md)实例进行分配进程,配置了instance的UIExtensionAbility实例,将每个实例独立一个进程。独立进程的场景下,UIExtensionAbility实例之间只能通过跨进程进行通信,但实例之间的状态不会彼此影响,安全性更高。
217
218**图7** instance模型配置示意图
219
220![uiextability-instance-processmodel](figures/uiextability-instance-processmodel.png)
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![uiextability-instance-example](figures/uiextability-instance-example.png)
299
300采用该进程模型,进程名格式为:
301process name [{bundleName}:{UIExtensionAbility的类型}: {实例后缀}]
302例如,process name [com.ohos.intentexecutedemo:xxx:n]。
303
304**图9** 进程模型展示
305
306![uiextability-instance-process-example](figures/uiextability-instance-process-example.png)
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.ets321
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```