• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 应用启动框架AppStartup
2
3
4## 概述
5
6应用启动时通常需要执行一系列初始化启动任务,如果将启动任务都放在应用主模块(即entry类型的[Module](../quick-start/application-package-overview.md#module类型))的[UIAbility](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md)组件的[onCreate](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#uiabilityoncreate)生命周期中,那么只能在主线程中依次执行,不但影响应用的启动速度,而且当启动任务过多时,任务之间复杂的依赖关系还会使得代码难以维护。
7
8AppStartup提供了一种简单高效的应用启动方式,可以支持任务的异步启动,加快应用启动速度。同时,通过在一个配置文件中统一设置多个启动任务的执行顺序以及依赖关系,让执行启动任务的代码变得更加简洁清晰、容易维护。
9
10## 运行机制
11
12启动框架支持以自动模式或手动模式执行启动任务,默认采用自动模式。在构造[AbilityStage组件容器](../reference/apis-ability-kit/js-apis-app-ability-abilityStage.md)过程中开始加载开发者配置的启动任务,并执行自动模式的启动任务。开发者也可以在AbilityStage创建完后调用[startupManager.run](../reference/apis-ability-kit/js-apis-app-appstartup-startupManager.md#startupmanagerrun)方法,执行手动模式的启动任务。
13
14  **图1** 启动框架执行时机
15  ![app-startup-procedure](figures/app-startup-procedure.png)
16
17
18## 支持的范围
19
20- 启动框架仅在entry的UIAbility启动时被拉起,ExtensionAbility和非entry模块的UIAbility启动时不会拉起启动框架。
21
22- 启动框架从API 18开始支持在[HSP](../quick-start/har-package.md)和[HAR](../quick-start/in-app-hsp.md)中配置启动任务。HSP和HAR的启动任务、so预加载任务无法主动配置为自动模式,但可以被entry中自动模式的启动任务、so预加载任务拉起。
23
24- 启动框架从API 18开始支持配置so预加载任务,so文件开发可以参考[Node-API](../napi/use-napi-process.md)创建Native C++工程。
25
26
27## 约束限制
28
29- 使用启动框架必须在entry类型[Module](../quick-start/application-package-overview.md#module类型)的[module.json5配置文件](../quick-start/module-configuration-file.md)中开启启动框架。
30
31- 启动任务之间或so预加载任务之间不允许存在循环依赖。
32
33
34## 开发流程
35
361. [定义启动框架配置文件](#定义启动框架配置文件):在资源文件目录下创建启动框架配置文件、添加启动任务的配置信息,并在[module.json5](../quick-start/module-configuration-file.md)配置文件中引用。
372. [设置启动参数](#设置启动参数):在启动参数文件中,设置超时时间和启动任务的监听器等参数。
383. [为每个待初始化功能组件添加启动任务](#为每个待初始化功能组件添加启动任务):通过实现[StartupTask](../reference/apis-ability-kit/js-apis-app-appstartup-startupTask.md)接口,启动框架将会按顺序执行初始化流程。
39
40## 开发步骤
41
42### 定义启动框架配置文件
43
441. 在应用主模块(即entry类型的[Module](../quick-start/application-package-overview.md#module类型))的“resources/base/profile”路径下,新建启动框架配置文件。文件名可以自定义,本文以"startup_config.json"为例。
45
462. 在启动框架配置文件startup_config.json中,依次添加各个启动任务和so预加载任务的配置信息。
47
48    假设当前应用启动框架共包含6个启动任务和6个so预加载任务,任务之间的依赖关系如下图所示。为了便于并发执行启动任务,单个启动任务文件包含的启动任务应尽量单一,本例中每个启动任务对应一个启动任务文件。不建议应用在so文件的加载回调中运行代码逻辑,so文件的加载不宜过长,否则会影响主线程的运行。
49
50    **图2** 启动任务与so预加载任务依赖关系图
51    ![app-startup](figures/app-startup.png)
52
53    1. 在“ets/startup”路径下,依次创建6个启动任务文件、以及一个公共的启动参数配置文件。文件名称必须确保唯一性。
54
55        1. 创建启动任务文件。本例中的6个文件名分别为StartupTask_001.ets~StartupTask_006.ets56        2. 参考[Node-API](../napi/use-napi-process.md)创建so文件。本例中的6个so文件名称分别为libentry_001.so~libentry_006.so57        3. 创建启动任务参数配置文件。本例中的文件名为StartupConfig.ets58
59    2. 在启动框架配置文件startup_config.json中,添加所有启动任务以及启动参数配置文件的相关信息。
60
61        应用主模块的startup_config.json文件示例如下:
62
63        ```json
64        {
65          "startupTasks": [
66            {
67              "name": "StartupTask_001",
68              "srcEntry": "./ets/startup/StartupTask_001.ets",
69              "dependencies": [
70                "StartupTask_002",
71                "StartupTask_003"
72              ],
73              "runOnThread": "taskPool",
74              "waitOnMainThread": false
75            },
76            {
77              "name": "StartupTask_002",
78              "srcEntry": "./ets/startup/StartupTask_002.ets",
79              "dependencies": [
80                "StartupTask_003",
81                "StartupTask_004"
82              ],
83              "runOnThread": "taskPool",
84              "waitOnMainThread": false
85            },
86            {
87              "name": "StartupTask_003",
88              "srcEntry": "./ets/startup/StartupTask_003.ets",
89              "dependencies": [
90                "StartupTask_004"
91              ],
92              "runOnThread": "taskPool",
93              "waitOnMainThread": false
94            },
95            {
96              "name": "StartupTask_004",
97              "srcEntry": "./ets/startup/StartupTask_004.ets",
98              "runOnThread": "taskPool",
99              "waitOnMainThread": false
100            },
101            {
102              "name": "StartupTask_005",
103              "srcEntry": "./ets/startup/StartupTask_005.ets",
104              "dependencies": [
105                "StartupTask_006"
106              ],
107              "runOnThread": "mainThread",
108              "waitOnMainThread": true,
109              "excludeFromAutoStart": true
110            },
111            {
112              "name": "StartupTask_006",
113              "srcEntry": "./ets/startup/StartupTask_006.ets",
114              "runOnThread": "mainThread",
115              "waitOnMainThread": false,
116              "excludeFromAutoStart": true
117            }
118          ],
119          "appPreloadHintStartupTasks": [
120            {
121              "name": "libentry_001",
122              "srcEntry": "libentry_001.so",
123              "dependencies": [
124                "libentry_002",
125                "libentry_003"
126              ],
127              "runOnThread": "taskPool"
128            },
129            {
130              "name": "libentry_002",
131              "srcEntry": "libentry_002.so",
132              "dependencies": [
133                "libentry_003",
134                "libentry_004"
135              ],
136              "runOnThread": "taskPool"
137            },
138            {
139              "name": "libentry_003",
140              "srcEntry": "libentry_003.so",
141              "dependencies": [
142                "libentry_004"
143              ],
144              "runOnThread": "taskPool"
145            },
146            {
147              "name": "libentry_004",
148              "srcEntry": "libentry_004.so",
149              "runOnThread": "taskPool"
150            },
151            {
152              "name": "libentry_005",
153              "srcEntry": "libentry_005.so",
154              "dependencies": [
155                "libentry_006"
156              ],
157              "runOnThread": "taskPool",
158              "excludeFromAutoStart": true
159            },
160            {
161              "name": "libentry_006",
162              "srcEntry": "libentry_006.so",
163              "runOnThread": "taskPool",
164              "excludeFromAutoStart": true
165            }
166          ],
167          "configEntry": "./ets/startup/StartupConfig.ets"
168        }
169        ```
170
171
172        **表1** startup_config.json配置文件标签说明
173
174        | 属性名称 | 含义 | 数据类型 | 是否可缺省 |
175        | -------- | -------- | -------- | -------- |
176        | startupTasks | 启动任务配置信息,标签说明详见下表。 | 对象数组 | 该标签可缺省,缺省值为空。 |
177        | appPreloadHintStartupTasks | 预加载so任务配置信息,标签说明详见下表。 | 对象数组 | 该标签可缺省,缺省值为空。 |
178        | configEntry | 启动参数配置文件所在路径。<br/>**说明:**<br/> HSP、HAR中不允许配置`configEntry`字段。 | 字符串 | 该标签不可缺省。 |
179
180
181        **表2** startupTasks标签说明
182
183        | 属性名称 | 含义 | 数据类型 | 是否可缺省 |
184        | -------- | -------- | -------- | -------- |
185        | name | 启动任务名称,可自定义,推荐与类名保持一致。 | 字符串 | 该标签不可缺省。 |
186        | srcEntry | 启动任务对应的文件路径。 | 字符串 | 该标签不可缺省。 |
187        | dependencies | 启动任务依赖的其他启动任务的类名数组。 | 对象数组 | 该标签可缺省,缺省值为空。 |
188        | excludeFromAutoStart | 是否排除自动模式,详细介绍可以查看[修改启动模式](#可选修改启动模式)。 <br/>-&nbsp;true:手动模式。 <br/>-&nbsp;false:自动模式。<br/>**说明:**<br/> HSP、HAR中startupTask里的excludeFromAutoStart标签必须配置为true。 | 布尔值 | 该标签可缺省,缺省值为false。 |
189        | runOnThread | 执行初始化所在的线程。<br/>-&nbsp;`mainThread`:在主线程中执行。<br/>-&nbsp;`taskPool`:在异步线程中执行。 | 字符串 | 该标签可缺省,缺省值为`mainThread`。 |
190        | waitOnMainThread | 主线程是否需要等待启动框架执行。当runOnThread取值为`taskPool`时,该字段生效。 <br/>-&nbsp;true:主线程等待启动框架执行完之后,才会加载应用首页。 <br/>-&nbsp;false:主线程不等待启动任务执行。 | 布尔值 | 该标签可缺省,缺省值为true。 |
191
192        **表3** appPreloadHintStartupTasks标签说明
193
194        | 属性名称 | 含义 | 数据类型 | 是否可缺省 |
195        | -------- | -------- | -------- | -------- |
196        | name | 预加载so文件名。 | 字符串 | 该标签不可缺省。 |
197        | srcEntry | 带后缀预加载so文件名。 | 字符串 | 该标签不可缺省。 |
198        | dependencies | 预加载任务依赖的其他预加载任务的so名数组。 | 对象数组 | 该标签可缺省,缺省值为空。 |
199        | excludeFromAutoStart | 是否排除自动模式,详细介绍可以查看[修改启动模式](#可选修改启动模式)。 <br/>-&nbsp;true:手动模式。 <br/>-&nbsp;false:自动模式。<br/>**说明:**<br/> HSP、HAR中appPreloadHintStartupTask的excludeFromAutoStart标签必须配置为true。 | 布尔值 | 该标签可缺省,缺省值为false。|
200        | runOnThread | 执行预加载所在的线程。<br/>-&nbsp;`taskPool`:在异步线程中执行。<br/>**说明:**<br/> so预加载只允许在`taskPool`线程执行。 | 字符串 | 该标签不可缺省。 |
201
202
203      3. 在[module.json5配置文件](../quick-start/module-configuration-file.md)的appStartup标签中,添加启动框架配置文件的索引。
204
205          module.json5示例代码如下。
206
207          ```json
208          {
209            "module": {
210              "name": "entry",
211              "type": "entry",
212              // ...
213              "appStartup": "$profile:startup_config", // 启动框架的配置文件
214              // ...
215            }
216          }
217          ```
218
219### 设置启动参数
220
221在启动参数配置文件(本文为“ets/startup/StartupConfig.ets”文件)中,使用[StartupConfigEntry](../reference/apis-ability-kit/js-apis-app-appstartup-startupConfigEntry.md)接口实现启动框架公共参数的配置,包括超时时间和启动任务的监听器等参数,其中需要用到如下接口:
222
223- [StartupConfig](../reference/apis-ability-kit/js-apis-app-appstartup-startupConfig.md):用于设置任务超时时间和启动框架的监听器。
224- [StartupListener](../reference/apis-ability-kit/js-apis-app-appstartup-startupListener.md):用于监听启动任务是否执行成功。
225
226
227
228```ts
229import { StartupConfig, StartupConfigEntry, StartupListener } from '@kit.AbilityKit';
230import { hilog } from '@kit.PerformanceAnalysisKit';
231import { BusinessError } from '@kit.BasicServicesKit';
232
233export default class MyStartupConfigEntry extends StartupConfigEntry {
234  onConfig() {
235    hilog.info(0x0000, 'testTag', `onConfig`);
236    let onCompletedCallback = (error: BusinessError<void>) => {
237      hilog.info(0x0000, 'testTag', `onCompletedCallback`);
238      if (error) {
239        hilog.info(0x0000, 'testTag', 'onCompletedCallback: %{public}d, message: %{public}s', error.code, error.message);
240      } else {
241        hilog.info(0x0000, 'testTag', `onCompletedCallback: success.`);
242      }
243    };
244    let startupListener: StartupListener = {
245      'onCompleted': onCompletedCallback
246    };
247    let config: StartupConfig = {
248      'timeoutMs': 10000,
249      'startupListener': startupListener
250    };
251    return config;
252  }
253}
254```
255
256### 为每个待初始化功能组件添加启动任务
257
258上述操作中已完成启动框架配置文件、启动参数的配置,还需要在每个功能组件对应的启动任务文件中,通过实现[StartupTask](../reference/apis-ability-kit/js-apis-app-appstartup-startupTask.md)来添加启动任务。其中,需要用到下面的两个方法:
259
260- [init](../reference/apis-ability-kit/js-apis-app-appstartup-startupTask.md#startuptaskinit):启动任务初始化。当该任务依赖的启动任务全部执行完毕,即onDependencyCompleted完成调用后,才会执行init方法对该任务进行初始化。
261- [onDependencyCompleted](../reference/apis-ability-kit/js-apis-app-appstartup-startupTask.md#startuptaskondependencycompleted):当前任务依赖的启动任务执行完成时,调用该方法。
262
263
264下面以[startup_config.json](#定义启动框架配置文件)中的StartupTask_001.ets文件为例,示例代码如下。开发者需要分别为每个待初始化功能组件添加启动任务。
265
266> **说明:**
267>
268> 由于StartupTask采用了[Sendable协议](../arkts-utils/arkts-sendable.md#sendable协议),在继承该接口时,必须添加Sendable注解。
269
270```ts
271import { StartupTask, common } from '@kit.AbilityKit';
272import { hilog } from '@kit.PerformanceAnalysisKit';
273
274@Sendable
275export default class StartupTask_001 extends StartupTask {
276  constructor() {
277    super();
278  }
279
280  async init(context: common.AbilityStageContext) {
281    hilog.info(0x0000, 'testTag', 'StartupTask_001 init.');
282    return 'StartupTask_001';
283  }
284
285  onDependencyCompleted(dependence: string, result: Object): void {
286    hilog.info(0x0000, 'testTag', 'StartupTask_001 onDependencyCompleted, dependence: %{public}s, result: %{public}s',
287      dependence, JSON.stringify(result));
288  }
289}
290```
291
292 ### (可选)HSP与HAR中使用启动框架
293
294 通常大型应用会有多个[HSP](../quick-start/har-package.md)和[HAR](../quick-start/in-app-hsp.md),本节将提供一个应用示例,以展示如何在HSP包和HAR包中使用启动框架。该示例应用包括两个HSP包(hsp1、hsp2)和一个HAR包(har1),并且包含启动任务和so预加载任务。
295
296 开发步骤如下:
297
298  1. 除应用主模块外,在HSP包和HAR包的“resources/base/profile”目录下创建启动框架配置文件,不同模块可以使用相同文件名,本文以"startup_config.json"为例。
299
300  2. 分别在各个模块的启动框架配置文件startup_config.json中, 添加对应的配置信息。
301
302        假设当前应用存在的启动任务与so预加载任务如下表所示。
303
304        **表4** 应用启动任务与so预加载任务说明
305        | 模块 | 启动任务 | so预加载任务 |
306        | ------- | -------------------------------- | -------------------------------- |
307        | entry | HAP_Task_01 | libentry_01 |
308        | hsp1 | HSP1_Task_01 <br/> HSP1_Task_02 | libhsp1_01 <br/> libhsp1_02 |
309        | hsp2 | HSP2_Task_01 | libhsp2_01 |
310        | har | HAR1_Task_01 | libhar1_01 |
311
312        **图3** 启动任务与so预加载依赖关系图
313        ![app-startup](figures/hsp-har-startup.png)
314
315        entry模块的startup_config.json可参考[定义启动框架配置文件](#定义启动框架配置文件),HSP与HAR的startup_config.json文件无法配置"configEntry"字段,以hsp1包配置文件为例,示例如下:
316
317        ```json
318        {
319          "startupTasks": [
320            {
321              "name": "HSP1_Task_01",
322              "srcEntry": "./ets/startup/HSP1_Task_01.ets",
323              "dependencies": [
324                "HSP1_Task_02",
325                "HAR1_Task_01"
326              ],
327              "runOnThread": "taskPool",
328              "waitOnMainThread": false,
329              "excludeFromAutoStart": true
330            }
331          ],
332          "appPreloadHintStartupTasks": [
333            {
334              "name": "libhsp1_01",
335              "srcEntry": "libhsp1_01.so",
336              "dependencies": [
337                "libhsp1_02",
338                "libhar1_01"
339              ],
340              "runOnThread": "taskPool",
341              "excludeFromAutoStart": true
342            }
343          ]
344        }
345        ```
346
347  3. 分别在各个模块的[module.json5配置文件](../quick-start/module-configuration-file.md)的appStartup标签中,添加启动框架配置文件的索引。
348
349        hsp1、hsp2以及har1的module.json5示例代码如下。
350
351        ```json
352        {
353          "module": {
354            "name": "hsp1",
355            "type": "shared",
356            // ...
357            "appStartup": "$profile:startup_config", // 启动框架的配置文件
358            // ...
359          }
360        }
361        ```
362        ```json
363        {
364          "module": {
365            "name": "hsp2",
366            "type": "shared",
367            // ...
368            "appStartup": "$profile:startup_config", // 启动框架的配置文件
369            // ...
370          }
371        }
372        ```
373        ```json
374        {
375          "module": {
376            "name": "har1",
377            "type": "har",
378            // ...
379            "appStartup": "$profile:startup_config", // 启动框架的配置文件
380            // ...
381          }
382        }
383        ```
384
385  其余步骤请参考[设置启动参数](#设置启动参数)和[为每个待初始化功能组件添加启动任务](#为每个待初始化功能组件添加启动任务)章节进行配置。
386
387
388### (可选)修改启动模式
389
390AppStartup分别提供了自动和手动两种方式来执行启动任务,entry模块中默认采用自动模式,开发者可以根据需要修改为手动模式,HSP与HAR只能配置为手动模式。
391
392- 自动模式:当AbilityStage组件容器完成创建后,自动执行启动任务。
393- 手动模式:在UIAbility完成创建后手动调用,来执行启动任务与so预加载任务。对于某些使用频率不高的模块,不需要应用最开始启动时就进行初始化。开发者可以选择将该部分启动任务修改为手动模式,在应用启动完成后调用[startupManager.run](../reference/apis-ability-kit/js-apis-app-appstartup-startupManager.md#startupmanagerrun)方法来执行启动任务与so预加载任务。
394
395下面以UIAbility的onCreate生命周期中为例,介绍如何采用手动模式来启动任务,示例代码如下。
396
397```ts
398import { AbilityConstant, UIAbility, Want, startupManager } from '@kit.AbilityKit';
399import { hilog } from '@kit.PerformanceAnalysisKit';
400import { BusinessError } from '@kit.BasicServicesKit';
401
402export default class EntryAbility extends UIAbility {
403  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
404    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate');
405    let startParams = ["StartupTask_005", "StartupTask_006"];
406    try {
407      startupManager.run(startParams).then(() => {
408        console.log('StartupTest startupManager run then, startParams = ');
409      }).catch((error: BusinessError) => {
410        console.info('StartupTest promise catch error, error = ' + JSON.stringify(error));
411        console.info('StartupTest promise catch error, startParams = '
412          + JSON.stringify(startParams));
413      })
414    } catch (error) {
415      let errMsg = JSON.stringify(error);
416      let errCode: number = error.code;
417      console.log('Startup catch error , errCode= ' + errCode);
418      console.log('Startup catch error ,error= ' + errMsg);
419    }
420  }
421
422  // ...
423}
424```
425
426开发者还可以在页面加载完成后,在页面中调用启动框架手动模式,示例代码如下。
427
428```ts
429import { startupManager } from '@kit.AbilityKit';
430
431@Entry
432@Component
433struct Index {
434  @State message: string = "手动模式";
435  @State startParams1: Array<string> = ["StartupTask_006"];
436  @State startParams2: Array<string> = ["libentry_006"];
437
438  build() {
439    RelativeContainer() {
440      Button(this.message)
441        .id('AppStartup')
442        .fontSize(20)
443        .fontWeight(FontWeight.Bold)
444        .onClick(() => {
445          if (!startupManager.isStartupTaskInitialized("StartupTask_006") ) { // 判断是否已经完成初始化
446            startupManager.run(this.startParams1)
447          }
448          if (!startupManager.isStartupTaskInitialized("libentry_006") ) {
449            startupManager.run(this.startParams2)
450          }
451        })
452        .alignRules({
453          center: {anchor: '__container__', align: VerticalAlign.Center},
454          middle: {anchor: '__container__', align: HorizontalAlign.Center}
455        })
456    }
457    .height('100%')
458    .width('100%')
459  }
460}
461```
462