UIAbility概述
UIAbility是一種包含用戶界面的應用組件,主要用于和用戶進行交互。UIAbility也是系統調度的單元,為應用提供窗口在其中繪制界面。 每一個UIAbility實例,都對應于一個最近任務列表中的任務。 一個應用可以有一個UIAbility,也可以有多個UIAbility,如下圖所示。例如瀏覽器應用可以通過一個UIAbility結合多頁面的形式讓用戶進行的搜索和瀏覽內容;而聊天應用增加一個“外賣功能”的場景,則可以將聊天應用中“外賣功能”的內容獨立為一個UIAbility,當用戶打開聊天應用的“外賣功能”,查看外賣訂單詳情,此時有新的聊天消息,即可以通過最近任務列表切換回到聊天窗口繼續進行聊天對話。 一個UIAbility可以對應于多個頁面,建議將一個獨立的功能模塊放到一個UIAbility中,以多頁面的形式呈現。例如新聞應用在瀏覽內容的時候,可以進行多頁面的跳轉使用。 圖1 單UIAbility應用和多UIAbility應用
UIAbility內頁面的跳轉和數據傳遞
UIAbility的數據傳遞包括有UIAbility內頁面的跳轉和數據傳遞、UIAbility間的數據跳轉和數據傳遞,本章節主要講解UIAbility內頁面的跳轉和數據傳遞。 在一個應用包含一個UIAbility的場景下,可以通過新建多個頁面來實現和豐富應用的內容。這會涉及到UIAbility內頁面的新建以及UIAbility內頁面的跳轉和數據傳遞。 打開DevEco Studio,選擇一個Empty Ability工程模板,創建一個工程,例如命名為MyApplication。
- 在src/main/ets/entryability目錄下,初始會生成一個UIAbility文件EntryAbility.ts。可以在EntryAbility.ts文件中根據業務需要實現UIAbility的生命周期回調內容。
- 在src/main/ets/pages目錄下,會生成一個Index頁面。這也是基于UIAbility實現的應用的入口頁面。可以在Index頁面中根據業務需要實現入口頁面的功能。
- 在src/main/ets/pages目錄下,右鍵New->Page,新建一個Second頁面,用于實現頁面間的跳轉和數據傳遞。
為了實現頁面的跳轉和數據傳遞,需要新建一個頁面。在原有Index頁面的基礎上,新建一個頁面,例如命名為Second.ets。 頁面間的導航可以通過頁面路由router模塊來實現。頁面路由模塊根據頁面url找到目標頁面,從而實現跳轉。通過頁面路由模塊,可以使用不同的url訪問不同的頁面,包括跳轉到UIAbility內的指定頁面、用UIAbility內的某個頁面替換當前頁面、返回上一頁面或指定的頁面等。具體使用方法請參見ohos.router (頁面路由)。
頁面跳轉和參數接收 在使用頁面路由之前,需要先導入router模塊,如下代碼所示。
import router from '@ohos.router';
頁面跳轉的幾種方式,根據需要選擇一種方式跳轉即可。
- 方式一:API9及以上,router.pushUrl()方法新增了mode參數,可以將mode參數配置為router.RouterMode.Single單實例模式和router.RouterMode.Standard多實例模式。在單實例模式下:如果目標頁面的url在頁面棧中已經存在同url頁面,離棧頂最近同url頁面會被移動到棧頂,移動后的頁面為新建頁,原來的頁面仍然存在棧中,頁面棧的元素數量不變;如果目標頁面的url在頁面棧中不存在同url頁面,按多實例模式跳轉,頁面棧的元素數量會加1。 說明 當頁面棧的元素數量較大或者超過32時,可以通過調用router.clear()方法清除頁面棧中的所有歷史頁面,僅保留當前頁面作為棧頂頁面
router.pushUrl({url: 'pages/Second',params: {src: 'Index頁面傳來的數據',}
}, router.RouterMode.Single)
- 方式二:API9及以上,router.replaceUrl()方法新增了mode參數,可以將mode參數配置為router.RouterMode.Single單實例模式和router.RouterMode.Standard多實例模式。在單實例模式下:如果目標頁面的url在頁面棧中已經存在同url頁面,離棧頂最近同url頁面會被移動到棧頂,替換當前頁面,并銷毀被替換的當前頁面,移動后的頁面為新建頁,頁面棧的元素數量會減1;如果目標頁面的url在頁面棧中不存在同url頁面,按多實例模式跳轉,頁面棧的元素數量不變。
router.replaceUrl({url: 'pages/Second',params: {src: 'Index頁面傳來的數據',}
}, router.RouterMode.Single)
已經實現了頁面的跳轉,接下來,在Second頁面中如何進行自定義參數的接收呢? 通過調用router.getParams()方法獲取Index頁面傳遞過來的自定義參數。
import router from '@ohos.router';@Entry
@Component
struct Second {@State src: string = (router.getParams() as Record<string, string>)['src'];// 頁面刷新展示...
}
效果示意如下圖所示。在Index頁面中,點擊“Next”后,即可從Index頁面跳轉到Second頁面,并在Second頁面中接收參數和進行頁面刷新展示。 圖2 Index頁面跳轉到Second頁面
頁面返回和參數接收
經常還會遇到一個場景,在Second頁面中,完成了一些功能操作之后,希望能返回到Index頁面,那我們要如何實現呢? 在Second頁面中,可以通過調用router.back()方法實現返回到上一個頁面,或者在調用router.back()方法時增加可選的options參數(增加url參數)返回到指定頁面。 說明
- 調用router.back()返回的目標頁面需要在頁面棧中存在才能正常跳轉。
- 例如調用router.pushUrl()方法跳轉到Second頁面,在Second頁面可以通過調用router.back()方法返回到上一個頁面。
- 例如調用router.clear()方法清空了頁面棧中所有歷史頁面,僅保留當前頁面,此時則無法通過調用router.back()方法返回到上一個頁面。
- 返回上一個頁面。
router.back();
- 返回到指定頁面。
router.back({ url: 'pages/Index' });
效果示意如下圖所示。在Second頁面中,點擊“Back”后,即可從Second頁面返回到Index頁面。 圖3 Second頁面返回到Index頁面
頁面返回可以根據業務需要增加一個詢問對話框。 即在調用router.back()方法之前,可以先調用router.enableBackPageAlert()方法開啟頁面返回詢問對話框功能。 說明
- router.enableBackPageAlert()方法開啟頁面返回詢問對話框功能,只針對當前頁面生效。例如在調用router.pushUrl()或者router.replaceUrl()方法,跳轉后的頁面均為新建頁面,因此在頁面返回之前均需要先調用router.enableBackPageAlert()方法之后,頁面返回詢問對話框功能才會生效。
- 如需關閉頁面返回詢問對話框功能,可以通過調用router.disableAlertBeforeBackPage()方法關閉該功能即可。
router.enableBackPageAlert({message: 'Message Info'
});router.back();
在Second頁面中,調用router.back()方法返回上一個頁面或者返回指定頁面時,根據需要繼續增加自定義參數,例如在返回時增加一個自定義參數src。
router.back({url: 'pages/Index',params: {src: 'Second頁面傳來的數據',}
})
從Second頁面返回到Index頁面。在Index頁面通過調用router.getParams()方法,獲取Second頁面傳遞過來的自定義參數。 說明
調用router.back()方法,不會新建頁面,返回的是原來的頁面,在原來頁面中@State聲明的變量不會重復聲明,以及也不會觸發頁面的aboutToAppear()生命周期回調,因此無法直接在變量聲明以及頁面的aboutToAppear()生命周期回調中接收和解析router.back()傳遞過來的自定義參數。 可以放在業務需要的位置進行參數解析。示例代碼在Index頁面中的onPageShow()生命周期回調中進行參數的解析。
import router from '@ohos.router';
@Entry
@Component
struct Index {@State src: string = '';onPageShow() {this.src = (router.getParams() as Record<string, string>)['src'];}// 頁面刷新展示...
}
效果示意圖如下圖所示。在Second頁面中,點擊“Back”后,即可從Second頁面返回到Index頁面,并在Index頁面中接收參數和進行頁面刷新展示。 圖4 Second頁面帶參數返回Index頁面
UIAbility的生命周期
當用戶瀏覽、切換和返回到對應應用的時候,應用中的UIAbility實例會在其生命周期的不同狀態之間轉換。 UIAbility類提供了很多回調,通過這些回調可以知曉當前UIAbility的某個狀態已經發生改變:例如UIAbility的創建和銷毀,或者UIAbility發生了前后臺的狀態切換。 例如從桌面點擊圖庫應用圖標,到啟動圖庫應用,應用的狀態經過了從創建到前臺展示的狀態變化。如下圖所示。 圖5 從桌面點擊圖庫應用圖標啟動應用
回到桌面,從最近任務列表,切換回到圖庫應用,應用的狀態經過了從后臺到前臺展示的狀態變化。如下圖所示。 圖6 從最近任務列表切換回到圖庫應用
在UIAbility的使用過程中,會有多種生命周期狀態。掌握UIAbility的生命周期,對于應用的開發非常重要。 為了實現多設備形態上的裁剪和多窗口的可擴展性,系統對組件管理和窗口管理進行了解耦。UIAbility的生命周期包括Create、Foreground、Background、Destroy四個狀態,WindowStageCreate和WindowStageDestroy為窗口管理器(WindowStage)在UIAbility中管理UI界面功能的兩個生命周期回調,從而實現UIAbility與窗口之間的弱耦合。如下圖所示。 圖7 UIAbility生命周期狀態
- Create狀態,在UIAbility實例創建時觸發,系統會調用onCreate回調。可以在onCreate回調中進行相關初始化操作。
import UIAbility from '@ohos.app.ability.UIAbility';
import window from '@ohos.window';export default class EntryAbility extends UIAbility {onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {// 應用初始化...}...
}
例如用戶打開電池管理應用,在應用加載過程中,在UI頁面可見之前,可以在onCreate回調中讀取當前系統的電量情況,用于后續的UI頁面展示。
- UIAbility實例創建完成之后,在進入Foreground之前,系統會創建一個WindowStage。每一個UIAbility實例都對應持有一個WindowStage實例。WindowStage為本地窗口管理器,用于管理窗口相關的內容,例如與界面相關的獲焦/失焦、可見/不可見。 可以在onWindowStageCreate回調中,設置UI頁面加載、設置WindowStage的事件訂閱。 在onWindowStageCreate(windowStage)中通過loadContent接口設置應用要加載的頁面,Window接口的使用詳見窗口開發指導。
import UIAbility from '@ohos.app.ability.UIAbility';
import window from '@ohos.window';export default class EntryAbility extends UIAbility {...onWindowStageCreate(windowStage: window.WindowStage) {// 設置UI頁面加載// 設置WindowStage的事件訂閱(獲焦/失焦、可見/不可見)...windowStage.loadContent('pages/Index', (err, data) => {...});}...
}
例如用戶打開游戲應用,正在打游戲的時候,有一個消息通知,打開消息,消息會以彈窗的形式彈出在游戲應用的上方,此時,游戲應用就從獲焦切換到了失焦狀態,消息應用切換到了獲焦狀態。對于消息應用,在onWindowStageCreate回調中,會觸發獲焦的事件回調,可以進行設置消息應用的背景顏色、高亮等操作。
- Foreground和Background狀態,分別在UIAbility切換至前臺或者切換至后臺時觸發。分別對應于onForeground回調和onBackground回調。 onForeground回調,在UIAbility的UI頁面可見之前,即UIAbility切換至前臺時觸發。可以在onForeground回調中申請系統需要的資源,或者重新申請在onBackground中釋放的資源。 onBackground回調,在UIAbility的UI頁面完全不可見之后,即UIAbility切換至后臺時候觸發。可以在onBackground回調中釋放UI頁面不可見時無用的資源,或者在此回調中執行較為耗時的操作,例如狀態保存等。
import UIAbility from '@ohos.app.ability.UIAbility';
import window from '@ohos.window';export default class EntryAbility extends UIAbility {...onForeground() {// 申請系統需要的資源,或者重新申請在onBackground中釋放的資源...}onBackground() {// 釋放UI頁面不可見時無用的資源,或者在此回調中執行較為耗時的操作// 例如狀態保存等...}
}
例如用戶打開地圖應用查看當前地理位置的時候,假設地圖應用已獲得用戶的定位權限授權。在UI頁面顯示之前,可以在onForeground回調中打開定位功能,從而獲取到當前的位置信息。 當地圖應用切換到后臺狀態,可以在onBackground回調中停止定位功能,以節省系統的資源消耗。
- 前面我們了解了UIAbility實例創建時的onWindowStageCreate回調的相關作用。對應于onWindowStageCreate回調。在UIAbility實例銷毀之前,則會先進入onWindowStageDestroy回調,我們可以在該回調中釋放UI頁面資源。
import UIAbility from '@ohos.app.ability.UIAbility';
import window from '@ohos.window';export default class EntryAbility extends UIAbility {...onWindowStageDestroy() {// 釋放UI頁面資源...}
}
例如在onWindowStageCreate中設置的獲焦/失焦等WindowStage訂閱事件。
- Destroy狀態,在UIAbility銷毀時觸發。可以在onDestroy回調中進行系統資源的釋放、數據的保存等操作。
import UIAbility from '@ohos.app.ability.UIAbility';
import window from '@ohos.window';export default class EntryAbility extends UIAbility {...onDestroy() {// 系統資源的釋放、數據的保存等...}
}
例如用戶使用應用的程序退出功能,會調用UIAbilityContext的terminalSelf()方法,從而完成UIAbility銷毀。或者用戶使用最近任務列表關閉該UIAbility實例時,也會完成UIAbility的銷毀。
UIAbility的啟動模式
對于瀏覽器或者新聞等應用,用戶在打開該應用,并瀏覽訪問相關內容后,回到桌面,再次打開該應用,顯示的仍然是用戶當前訪問的界面。 對于應用的分屏操作,用戶希望使用兩個不同應用(例如備忘錄應用和圖庫應用)之間進行分屏,也希望能使用同一個應用(例如備忘錄應用自身)進行分屏。 對于文檔應用,用戶從文檔應用中打開一個文檔內容,回到文檔應用,繼續打開同一個文檔,希望打開的還是同一個文檔內容。 基于以上場景的考慮,UIAbility當前支持singleton(單實例模式)、multiton(多實例模式)和specified(指定實例模式)3種啟動模式。 對啟動模式的詳細說明如下:
- singleton(單實例模式)當用戶打開瀏覽器或者新聞等應用,并瀏覽訪問相關內容后,回到桌面,再次打開該應用,顯示的仍然是用戶當前訪問的界面。 這種情況下可以將UIAbility配置為singleton(單實例模式)。每次調用startAbility()方法時,如果應用進程中該類型的UIAbility實例已經存在,則復用系統中的UIAbility實例,系統中只存在唯一一個該UIAbility實例。 即在最近任務列表中只存在一個該類型的UIAbility實例。
- multiton(多實例模式)用戶在使用分屏功能時,希望使用兩個不同應用(例如備忘錄應用和圖庫應用)之間進行分屏,也希望能使用同一個應用(例如備忘錄應用自身)進行分屏。 這種情況下可以將UIAbility配置為multiton(多實例模式)。每次調用startAbility()方法時,都會在應用進程中創建一個該類型的UIAbility實例。 即在最近任務列表中可以看到有多個該類型的UIAbility實例。
- specified(指定實例模式)用戶打開文檔應用,從文檔應用中打開一個文檔內容,回到文檔應用,繼續打開同一個文檔,希望打開的還是同一個文檔內容;以及在文檔應用中新建一個新的文檔,每次新建文檔,希望打開的都是一個新的空白文檔內容。 這種情況下可以將UIAbility配置為specified(指定實例模式)。在UIAbility實例新創建之前,允許開發者為該實例創建一個字符串Key,新創建的UIAbility實例綁定Key之后,后續每次調用startAbility方法時,都會詢問應用使用哪個Key對應的UIAbility實例來響應startAbility請求。如果匹配有該UIAbility實例的Key,則直接拉起與之綁定的UIAbility實例,否則創建一個新的UIAbility實例。運行時由UIAbility內部業務決定是否創建多實例。
singleton啟動模式
singleton啟動模式,也是默認情況下的啟動模式。 singleton啟動模式,每次調用startAbility()啟動UIAbility時,如果應用進程中該類型的UIAbility實例已經存在,則復用系統中的UIAbility實例,系統中只存在唯一一個該UIAbility實例。 singleton啟動模式的開發使用,在module.json5文件中的“launchType”字段配置為“singleton”即可。
{"module": {..."abilities": [{"launchType": "singleton",...}]}
}
multiton啟動模式
multiton啟動模式,每次調用startAbility()方法時,都會在應用進程中創建一個該類型的UIAbility實例。 multiton啟動模式的開發使用,在module.json5文件中的“launchType”字段配置為“multiton”即可。
{"module": {..."abilities": [{"launchType": "multiton",...}]}
}
specified啟動模式
specified啟動模式,根據業務需要是否創建一個新的UIAbility實例。在UIAbility實例創建之前,會先進入AbilityStage的onAcceptWant回調,在onAcceptWant回調中為每一個UIAbility實例創建一個Key,后續每次調用startAbility()方法創建該類型的UIAbility實例都會詢問使用哪個Key對應的UIAbility實例來響應startAbility()請求。 specified啟動模式的開發使用的步驟如下所示。 在module.json5文件中的“launchType”字段配置為“specified”。
{"module": {..."abilities": [{"launchType": "specified",...}]}
}
- 在調用startAbility()方法的want參數中,增加一個自定義參數來區別UIAbility實例,例如增加一個“instanceKey”自定義參數。
// 在啟動指定實例模式的UIAbility時,給每一個UIAbility實例配置一個獨立的Key標識
function getInstance() {...
}
let context:common.UIAbilityContext = ...; // context為調用方UIAbility的UIAbilityContext
let want: Want = {deviceId: '', // deviceId為空表示本設備bundleName: 'com.example.myapplication',abilityName: 'SpecifiedAbility',moduleName: 'specified', // moduleName非必選parameters: { // 自定義信息instanceKey: getInstance(),},
}
context.startAbility(want).then(() => {...
}).catch((err: BusinessError) => {...
})
- 在被拉起方UIAbility對應的AbilityStage的onAcceptWant生命周期回調中,解析傳入的want參數,獲取“instanceKey”自定義參數。根據業務需要返回一個該UIAbility實例的字符串Key標識。如果之前啟動過此Key標識的UIAbility,則會將之前的UIAbility拉回前臺并獲焦,而不創建新的實例,否則創建新的實例并啟動。
onAcceptWant(want: want): string {// 在被啟動方的AbilityStage中,針對啟動模式為specified的UIAbility返回一個UIAbility實例對應的一個Key值// 當前示例指的是device Module的EntryAbilityif (want.abilityName === 'MainAbility') {return `DeviceModule_MainAbilityInstance_${want.parameters.instanceKey}`;}return '';
}
例如在文檔應用中,可以對不同的文檔實例內容綁定不同的Key值。當每次新建文檔的時候,可以傳入不同的新Key值(如可以將文件的路徑作為一個Key標識),此時AbilityStage中啟動UIAbility時都會創建一個新的UIAbility實例;當新建的文檔保存之后,回到桌面,或者新打開一個已保存的文檔,回到桌面,此時再次打開該已保存的文檔,此時AbilityStage中再次啟動該UIAbility時,打開的仍然是之前原來已保存的文檔界面。 操作舉例如下表所示。
操作序號 | 文檔內容 | UIAbility實例 |
---|---|---|
1 | 打開文件A | 對應UIAbility實例1 |
2 | 關閉打開文件A的進程,回到桌面,再次打開文件A | 對應UIAbility實例2 |
3 | 打開文件B | 對應UIAbility實例3 |
4 | 再次打開文件A | 對應UIAbility實例2 |
全文就主要講解了HarmonyOS開發中的應用程序框架——UIAbility的使用實例,更多鴻蒙開發可以關注我或主頁了解詳情內容。