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![uiextability-bundle-processmodel](figures/uiextability-bundle-processmodel.png)
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![uiextension-bundle-example](figures/uiextability-bundle-example.png)
112
113采用该进程模型,进程名格式为:
114process name [{bundleName}:{UIExtensionAbility的类型}]
115例如,process name [com.ohos.intentexecutedemo:xxx]。
116**图3** 进程模型展示
117![uiextension-bundle-process-example](figures/uiextability-bundle-process-example.png)
118
119### 同UIExtensionAbility类的所有UIExtensionAbility共进程
120根据[UIExtensionAbility](../reference/apis-ability-kit/js-apis-app-ability-uiExtensionAbility.md)类进行分配进程,拉起多个同样的UIExtensionAbility实例时,这些实例将配置在同一个进程中。将同UIExtensionAbility类配置在同一个进程下,方便应用针对UIExtensionAbility类型对实例进行管理;
121
122**图4** type模型配置示意图
123![uiextability-type-processmodel](figures/uiextability-type-processmodel.png)
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![uiextability-type-example](figures/uiextability-type-example.png)
200
201采用该进程模型,进程名格式为:
202process name [{bundleName}:{UIExtensionAbility名}]
203例如,process name [com.ohos.intentexecutedemo:xxx]。
204**图6** 进程模型展示
205![uiextability-type-process-example](figures/uiexteability-type-precess-example.png)
206
207### UIExtensionAbility实例独立进程
208根据[UIExtensionAbility](../reference/apis-ability-kit/js-apis-app-ability-uiExtensionAbility.md)实例进行分配进程,配置了instance的UIExtensionAbility实例,将每个实例独立一个进程。独立进程的场景下,UIExtensionAbility实例之间只能通过跨进程进行通信,但实例之间的状态不会彼此影响,安全性更高;
209
210**图7** instance模型配置示意图
211![uiextability-instance-processmodel](figures/uiextability-instance-processmodel.png)
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![uiextability-instance-example](figures/uiextability-instance-example.png)
289
290采用该进程模型,进程名格式为:
291process name [{bundleName}:{UIExtensionAbility的类型}: {实例后缀}]
292例如,process name [com.ohos.intentexecutedemo:xxx:n]。
293**图9** 进程模型展示
294![uiextability-instance-process-example](figures/uiextability-instance-process-example.png)
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.ets309
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```