1.概述
??在我們應(yīng)用啟動(dòng)時(shí),通常需要初始化一些啟動(dòng)任務(wù),如果我們將所有的任務(wù)都放置在應(yīng)用主模塊(也就是entry類型的Module)它的UIAbility組件的onCreate生命周期中,那么任務(wù)只能在主線程中依次執(zhí)行,這樣就會(huì)影響應(yīng)用的啟動(dòng)速度,當(dāng)啟動(dòng)任務(wù)過多時(shí),任務(wù)之間復(fù)雜的依賴關(guān)系還會(huì)使得代碼難以維護(hù)。官方提供了一種簡(jiǎn)單高效的應(yīng)用啟動(dòng)框架AppStartup。
2.應(yīng)用啟動(dòng)流程
-
進(jìn)程啟動(dòng)過程:
- 由系統(tǒng)服務(wù)通過孵化進(jìn)程拉起應(yīng)用主進(jìn)程并創(chuàng)建出相應(yīng)的運(yùn)行環(huán)境。
- 應(yīng)用再根據(jù)實(shí)際情況請(qǐng)求系統(tǒng)服務(wù)。
- 啟動(dòng)應(yīng)用所需的其他進(jìn)程。
注意:每個(gè)應(yīng)用至多并存三類進(jìn)程(主進(jìn)程、Extension類進(jìn)程、Render進(jìn)程),應(yīng)用中所有的進(jìn)程都是由系統(tǒng)創(chuàng)建和管理的。
-
模塊啟動(dòng)順序:
- 首先拉起應(yīng)用的入口模塊,該模塊加載時(shí)會(huì)創(chuàng)建一個(gè)AbilityStage實(shí)例
(這里我們可以理解為Android中的Application),可以對(duì)該模塊進(jìn)行初始化等操作。 - 模塊初始完成后,會(huì)拉起相應(yīng)的入口UIAbility(這里我們可以理解為Android中的MainActivity)。
- UIAbility加載完成后會(huì)生成一個(gè)WindowStage類實(shí)例并與之綁定(這里我們可以理解為Android中的PhoneWindow)。WindowStage發(fā)揮了應(yīng)用進(jìn)程內(nèi)窗口管理器的作用,UIAbility通過它持有一個(gè)窗口,在該窗口上加載出首個(gè)ArkUI頁(yè)面(這里我們可以理解為Android中的布局文件R.layout.xxx),從而在設(shè)備上呈現(xiàn)。
- 首先拉起應(yīng)用的入口模塊,該模塊加載時(shí)會(huì)創(chuàng)建一個(gè)AbilityStage實(shí)例
-
啟動(dòng)流程總結(jié)
??系統(tǒng)服務(wù)孵化應(yīng)用進(jìn)程,拉起應(yīng)用主進(jìn)程,其次拉起應(yīng)用入口模塊,此時(shí)就會(huì)實(shí)例化AbilityState實(shí)例,拉起應(yīng)用的入口UIAbility,執(zhí)行其生命周期,當(dāng)onCreate執(zhí)行完成后會(huì)執(zhí)行onWindowStageCreate會(huì)創(chuàng)建WindowState實(shí)例與之綁定,它是應(yīng)用進(jìn)程的窗口管理器,可以加載ArkUI頁(yè)面。
3.AppStartup介紹
??它是一種簡(jiǎn)單高效的應(yīng)用啟動(dòng)方式,可以支持任務(wù)的異步啟動(dòng),加快應(yīng)用啟動(dòng)速度。同時(shí),通過在一個(gè)配置文件中統(tǒng)一設(shè)置多個(gè)啟動(dòng)任務(wù)的執(zhí)行順序以及依賴關(guān)系,讓執(zhí)行啟動(dòng)任務(wù)的代碼變得更加簡(jiǎn)潔清晰、容易維護(hù)。
功能特性
- 支持任務(wù)的異步啟動(dòng)。
- 配置文件統(tǒng)一配置任務(wù)的執(zhí)行順序及依賴關(guān)系。
- 支持自動(dòng) / 手動(dòng) 執(zhí)行啟動(dòng)任務(wù)。
執(zhí)行流程圖

image.png
使用限制
- 啟動(dòng)框架只支持在entry類型的Module下的UIAbility中使用。
- 啟動(dòng)任務(wù)之間不允許存在循環(huán)依賴。
使用流程
1. 定義啟動(dòng)框架配置文件:在資源文件目錄下創(chuàng)建啟動(dòng)框架配置文件、添加啟動(dòng)任務(wù)的配置信息,并在module.json5配置文件中引用。
-
- 在應(yīng)用主模塊(即entry類型的Module)的“resources/base/profile”路徑下,新建啟動(dòng)框架配置文件。文件名可以自定義,本文以"startup_config.json"為例。
{ "startupTasks": [ { "name":"StartupTask_001", //啟動(dòng)任務(wù)名稱 "srcEntry": "./ets/startup/StartupTask_001.ets", //啟動(dòng)任務(wù)對(duì)應(yīng)的文件路徑 "dependencies": [ //啟動(dòng)任務(wù)依賴的其他啟動(dòng)任務(wù)的類名數(shù)組 "StartupTask_002" ], "runOnThread": "taskPool", //執(zhí)行初始化所在的線程,默認(rèn)主線程,mainThread:在主線程中執(zhí)行。- taskPool:在異步線程中執(zhí)行 "waitOnMainThread": false //主線程是否需要等待啟動(dòng)框架執(zhí)行 }, { "name":"StartupTask_002", "srcEntry": "./ets/startup/StartupTask_002.ets", "dependencies": [], "runOnThread": "taskPool", "waitOnMainThread": false }, { "name":"StartupTask_003", "srcEntry": "./ets/startup/StartupTask_003.ets", "dependencies": [], "excludeFromAutoStart": true, //設(shè)置啟動(dòng)模式,默認(rèn)為false, true:手動(dòng)模式, false:自動(dòng)模式 "runOnThread": "taskPool", "waitOnMainThread": false } ], "configEntry": "./ets/startup/StartupConfig.ets" } - 2.在啟動(dòng)框架配置文件startup_config.json中,依次添加各個(gè)啟動(dòng)任務(wù)的配置信息。
{ "module": { "name": "entry", "type": "entry", "abilities": [ { "name": "EntryAbility", "srcEntry": "./ets/entryability/EntryAbility.ets", ... } ] "appStartup": "$profile:startup_config" //配置AppStartUp的啟動(dòng)項(xiàng)任務(wù)文件 } }
2. 設(shè)置啟動(dòng)參數(shù):在啟動(dòng)參數(shù)文件中,設(shè)置超時(shí)時(shí)間和啟動(dòng)任務(wù)的監(jiān)聽器等參數(shù)。
-
- 創(chuàng)建啟動(dòng)參數(shù)配置文件,使用StartupConfigEntry接口實(shí)現(xiàn)啟動(dòng)框架公共參數(shù)的配置,包括超時(shí)時(shí)間和啟動(dòng)任務(wù)的監(jiān)聽器等參數(shù),其中需要用到如下接口:
- StartupConfig:用于設(shè)置任務(wù)超時(shí)時(shí)間和啟動(dòng)框架的監(jiān)聽器。
- StartupListener:用于監(jiān)聽啟動(dòng)任務(wù)是否執(zhí)行成功。
export default class MyStartupConfigEntry extends StartupConfigEntry{ onConfig(): StartupConfig { console.info('ych' , `onConfig`) //監(jiān)聽啟動(dòng)任務(wù)執(zhí)行的回掉,成功/失敗 let onCompletedCallback = (error: BusinessError<void>) => { console.info('ych' , `onCompletedCallback`) if (error) { console.error('ych' , `onCompletedCallback error: ${error.message}`) } else { console.info('ych' , `onCompletedCallback: success.`) } }; let startupListener: StartupListener = { 'onCompleted': onCompletedCallback }; // 任務(wù)參數(shù)配置 let config: StartupConfig = { 'timeoutMs': 5000, // 任務(wù)超時(shí)時(shí)間 'startupListener': startupListener // 啟動(dòng)任務(wù)監(jiān)聽器 }; return config; } }
3. 為每個(gè)待初始化組件添加啟動(dòng)任務(wù):通過實(shí)現(xiàn)StartupTask接口,啟動(dòng)框架將會(huì)按順序執(zhí)行初始化流程。
注意:
* 1. 啟動(dòng)任務(wù)必須實(shí)現(xiàn) StartupTask 類
* 2. 由于StartupTask采用了 Sendable 協(xié)議(線程間通信對(duì)象),在繼承該接口時(shí),必須添加Sendable注解。
* 3. 如果任務(wù)不是應(yīng)用創(chuàng)建時(shí)必須初始化,那么我們可以使用手動(dòng)初始化,直接調(diào)用
* 3.1.
* try {
* startupManager.run(["StartupTask_003"]).then(()=>{
* console.error('ych' , `StartupTask_003 啟動(dòng)完成`)
* }).catch((err:BusinessError)=>{
* console.error('ych' , `StartupTask_003 啟動(dòng)失敗 ${err.message}`)
* })
* }catch (error){
* console.error('ych' , `StartupTask_003 啟動(dòng)失敗 ${error.message}`)
* }
* 3.2.需要在resource/config/startup.json中配置該任務(wù)為非必須任務(wù)。也就是 "excludeFromAutoStart": true
@Sendable
export default class StartupTask_001 extends StartupTask{
constructor() {
super();
}
/**
* TODO: 啟動(dòng)任務(wù)初始化。當(dāng)該任務(wù)依賴的啟動(dòng)任務(wù)全部執(zhí)行完畢,即onDependencyCompleted完成調(diào)用后,才會(huì)執(zhí)行init方法對(duì)該任務(wù)進(jìn)行初始化。
* 總結(jié):
* 1.如果該任務(wù)無依賴任務(wù),則不會(huì)執(zhí)行onDependencyCompleted方法,直接執(zhí)行init方法。
* 2.如果該任務(wù)有依賴任務(wù),則先執(zhí)行onDependencyCompleted方法,再執(zhí)行init方法。
*/
async init(context: common.AbilityStageContext){
this.myStartupTask_001()
return "StartupTask_001"
}
onDependencyCompleted(dependence: string, result: Object): void {
console.error('ych', `依賴的任務(wù)名稱為:${dependence} --- 結(jié)果:${result.valueOf()}`,
dependence, JSON.stringify(result));
}
myStartupTask_001(){
console.error('ych' , `StartupTask_001開始初始化`)
}
}
使用注意:
- 1. 啟動(dòng)任務(wù)必須實(shí)現(xiàn) StartupTask 類
- 由于StartupTask采用了 Sendable 協(xié)議(線程間通信對(duì)象),在繼承該接口時(shí),必須添加Sendable注解。
-
如果任務(wù)不是應(yīng)用創(chuàng)建時(shí)必須啟動(dòng),那么我們可以使用手動(dòng)啟動(dòng),直接調(diào)用
- 3.1.手動(dòng)啟動(dòng)任務(wù)
try { startupManager.run(["StartupTask_001"]).then(()=>{ console.error('ych' , `StartupTask_001 啟動(dòng)完成`) }).catch((err:BusinessError)=>{ console.error('ych' , `StartupTask_001 啟動(dòng)失敗 ${err.message}`) }) }catch (error){ console.error('ych' , `StartupTask_001 啟動(dòng)失敗 ${error.message}`) }- 3.2.需要在resource/config/startup.json中配置該任務(wù)為非必須任務(wù)。也就是 "excludeFromAutoStart": true
{ "startupTasks": [ { "name":"StartupTask_001", "srcEntry": "./ets/startup/StartupTask_001.ets", "dependencies": [], "runOnThread": "taskPool", "excludeFromAutoStart": true, //設(shè)置啟動(dòng)模式,默認(rèn)為false, true:手動(dòng)模式, false:自動(dòng)模式 "waitOnMainThread": false }, "configEntry": "./ets/startup/StartupConfig.ets" }
-