HarmonyOS Next應(yīng)用開發(fā):UIAbility組件介紹
UIAbility是Stage模型中的組件類型名间螟,包含UI,提供展示UI的能力损肛,主要用于和用戶交互厢破。
UIAbility的設(shè)計理念:
1.原生支持應(yīng)用組件級的跨端遷移和多端協(xié)同。
2.支持多設(shè)備和多窗口形態(tài)治拿。
UIAbility劃分原則與建議:
UIAbility組件是系統(tǒng)調(diào)度的基本單元摩泪,為應(yīng)用提供繪制界面的窗口。一個應(yīng)用可以包含一個或多個UIAbility組件劫谅。
每一個UIAbility組件實例都會在最近任務(wù)列表中顯示一個對應(yīng)的任務(wù)见坑。
對于開發(fā)者而言嚷掠,可以根據(jù)具體場景選擇單個還是多個UIAbility,劃分建議如下:
如果開發(fā)者希望在任務(wù)視圖中看到一個任務(wù)荞驴,則建議使用一個UIAbility叠国,多個頁面的方式。
如果開發(fā)者希望在任務(wù)視圖中看到多個任務(wù)戴尸,或者需要同時開啟多個窗口粟焊,則建議使用多個UIAbility開發(fā)不同的模塊功能。
UIAbility組件基本用法
創(chuàng)建一個UIAbility需要繼承UIAbility孙蒙,并且UIAbility在啟動過程中项棠,需要指定啟動頁面,否則應(yīng)用啟動后會因為沒有默認加載頁面而導(dǎo)致白屏挎峦∠阕罚可以在UIAbility的onWindowStageCreate()生命周期回調(diào)中,通過WindowStage對象的loadContent()方法設(shè)置啟動頁面坦胶。
import UIAbility from '@ohos.app.ability.UIAbility';
import window from '@ohos.window';
export default class EntryAbility extends UIAbility {
onWindowStageCreate(windowStage: window.WindowStage) {
windowStage.loadContent('pages/Index', (err, data) => {
});
}
}
為使應(yīng)用能夠正常使用UIAbility透典,需要在module.json5配置文件的abilities標(biāo)簽中聲明UIAbility的名稱、入口顿苇、標(biāo)簽等相關(guān)信息峭咒。
"abilities": [
{
"name": "EntryAbility",// UIAbility組件的名稱
"srcEntry": "./ets/entryability/EntryAbility.ts",// UIAbility組件的代碼路徑
"description": "$string:EntryAbility_desc",// UIAbility組件的描述信息
"icon": "$media:icon",// UIAbility組件的圖標(biāo)
"label": "$string:EntryAbility_label",// UIAbility組件的標(biāo)簽
"startWindowIcon": "$media:icon",// UIAbility組件啟動頁面圖標(biāo)資源文件的索引
"startWindowBackground": "$color:start_window_background",// UIAbility組件啟動頁面背景顏色資源文件的索引
"exported": true,// UIAbility組件是否可以被其他應(yīng)用調(diào)用
"skills": [// UIAbility組件能夠接收的Want特征集,為數(shù)組格式
{
"entities": [
"entity.system.home"
],
"actions": [
"action.system.home"
]
}
]
}
]
Stage模型支持對組件配置入口圖標(biāo)和入口標(biāo)簽纪岁。入口圖標(biāo)和入口標(biāo)簽會顯示在桌面上凑队。
入口圖標(biāo)需要在 module.json5配置文件中配置,除了需要配置icon與label字段幔翰,還需要在skills標(biāo)簽下面的entities中添加"entity.system.home"漩氨、actions中添加"action.system.home"。同一個應(yīng)用有多個UIAbility配置上述字段時遗增,桌面上會顯示出多個圖標(biāo)叫惊,分別對應(yīng)各自的UIAbility。
如果abilities標(biāo)簽下聲明的UIAbility的skills標(biāo)簽都為空做修,那么此應(yīng)用為無圖標(biāo)應(yīng)用霍狰,系統(tǒng)對無圖標(biāo)應(yīng)用實施嚴格管控,防止一些惡意應(yīng)用故意配置無桌面應(yīng)用圖標(biāo)缓待,導(dǎo)致用戶找不到軟件所在的位置蚓耽,無法操作卸載應(yīng)用,在一定程度上保證用戶終端設(shè)備的安全旋炒。除預(yù)置應(yīng)用外步悠,其他應(yīng)用不支持隱藏桌面圖標(biāo)。
UIAbility間的跳轉(zhuǎn)和數(shù)據(jù)傳遞
啟動應(yīng)用內(nèi)的UIAbility
創(chuàng)建兩個UIAbility瘫镇,分別為EntryAbility和SecondAbility鼎兽。在EntryAbility下的頁面Index中獲取UIAbility實例的上下文信息答姥,通過UIAbilityContext的startAbility()方法啟動UIAbility,startAbility()方法中的Want攜帶了啟動Ability的信息谚咬。
import Want from '@ohos.app.ability.Want';
import common from '@ohos.app.ability.common';
import hilog from [<u>'@ohos.hilog';</u>](mailto:'@ohos.hilog';)
const TAG: string = 'IndexPage';
const DOMAIN_NUMBER: number = 0xFF00;
@Entry
@Component
struct Index {
build() {
Row() {
Column() {
Button('點擊')
.margin({
top: 20
})
.width('50%')
.height('5%')
.onClick(()=>{
// context為Ability對象的成員鹦付,在非Ability對象內(nèi)部調(diào)用需要
let context = getContext(this) as common.UIAbilityContext;
// 將Context對象傳遞過去
let wantInfo: Want = {
deviceId: '', // deviceId為空表示本設(shè)備
bundleName: 'com.loop.hello',
moduleName: 'entry', // moduleName非必選
abilityName: 'SecondAbility',
parameters: {
// 自定義信息
info: '來自EntryAbility Index頁面'
},
};
// context為調(diào)用方UIAbility的UIAbilityContext
context.startAbility(wantInfo).then(() => {
hilog.info(DOMAIN_NUMBER, TAG, 'startAbility success.');
}).catch((error) => {
hilog.error(DOMAIN_NUMBER, TAG, 'startAbility failed.');
});
})
}
.width('100%')
}
.height('100%')
}
}
在SecondAbility的onCreate()或者onNewWant()生命周期回調(diào)文件中接收EntryAbility傳遞過來的參數(shù)。
import UIAbility from '@ohos.app.ability.UIAbility';
import hilog from [<u>'@ohos.hilog';</u>](mailto:'@ohos.hilog';)
export default class SecondAbility extends UIAbility {
onCreate(want, launchParam) {
// 接收調(diào)用方UIAbility傳過來的參數(shù)]
let info = want?.parameters?.info;
hilog.info(0x0000, 'SecondAbility', '%{public}s', `want param: ${info}`);
}
}
啟動應(yīng)用內(nèi)的UIAbility并獲取返回結(jié)果
在一個EntryAbility啟動另外一個SecondAbility時择卦,希望在被啟動的SecondAbility完成相關(guān)業(yè)務(wù)后敲长,能將結(jié)果返回給調(diào)用方。在EntryAbility中秉继,調(diào)用startAbilityForResult()接口啟動SecondAbility祈噪,異步回調(diào)中的data用于接收SecondAbility停止自身后返回給EntryAbility的信息。
import Want from '@ohos.app.ability.Want';
import common from '@ohos.app.ability.common';
import hilog from '@ohos.hilog';
import promptAction from '@ohos.promptAction';
const TAG: string = 'IndexPage';
const DOMAIN_NUMBER: number = 0xFF00;
@Entry
@Component
struct Index {
build() {
Row() {
Column() {
Button('點擊')
.margin({
top: 20
})
.width('50%')
.height('5%')
.onClick(()=>{
// context為Ability對象的成員尚辑,在非Ability對象內(nèi)部調(diào)用需要
let context = getContext(this) as common.UIAbilityContext;
const RESULT_CODE: number = 1001;
// 將Context對象傳遞過去
let wantInfo: Want = {
deviceId: '', // deviceId為空表示本設(shè)備
bundleName: 'com.loop.hello',
moduleName: 'entry', // moduleName非必選
abilityName: 'SecondAbility',
parameters: {
// 自定義信息
info: '來自EntryAbility Index頁面'
},
};
// context為調(diào)用方UIAbility的UIAbilityContext
context.startAbilityForResult(wantInfo).then((data) => {
if (data?.resultCode === RESULT_CODE) {
// 解析被調(diào)用方UIAbility返回的信息
let info = data.want?.parameters?.info;
hilog.info(DOMAIN_NUMBER, TAG, JSON.stringify(info) ?? '');
if (info !== null) {
promptAction.showToast({
message: JSON.stringify(info)
});
}
}
hilog.info(DOMAIN_NUMBER, TAG, 'startAbility success.');
}).catch((error) => {
hilog.error(DOMAIN_NUMBER, TAG, 'startAbility failed.');
});
})
}
.width('100%')
}
.height('100%')
}
}
在SecondAbility停止自身時辑鲤,需要調(diào)用terminateSelfWithResult()方法,入?yún)bilityResult為SecondAbility需要返回給EntryAbility的信息杠茬。
import common from '@ohos.app.ability.common'
import hilog from '@ohos.hilog'
const TAG: string = 'SecondPage';
const DOMAIN_NUMBER: number = 0xFF00;
@Entry
@Component
struct SecondPage {
@State message: string = 'SecondPage'
build() {
Row() {
Column() {
Text(this.message)
.fontSize(50)
.fontWeight(FontWeight.Bold)
.onClick(()=>{
let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; // UIAbilityContext
const RESULT_CODE: number = 1001;
let abilityResult: common.AbilityResult = {
resultCode: RESULT_CODE,
want: {
bundleName: 'com.loop.hello',
moduleName: 'entry', // moduleName非必選
abilityName: 'EntryAbility',
parameters: {
info: '來自SecondAbility SecondPage頁面'
},
},
};
context.terminateSelfWithResult(abilityResult, (err) => {
if (err.code) {
hilog.error(DOMAIN_NUMBER, TAG, `Failed to terminate self with result. Code is ${err.code}, message is ${err.message}`);
return;
}
});
})
}
.width('100%')
}
.height('100%')
}
}
FuncAbility停止自身后月褥,EntryAbility通過startAbilityForResult()方法回調(diào)接收被FuncAbility返回的信息,RESULT_CODE需要與前面的數(shù)值保持一致瓢喉。
啟動UIAbility的指定頁面
一個UIAbility可以對應(yīng)多個頁面宁赤,在不同的場景下啟動該UIAbility時需要展示不同的頁面,例如從一個UIAbility的頁面中跳轉(zhuǎn)到另外一個UIAbility時灯荧,希望啟動目標(biāo)UIAbility的指定頁面礁击。
UIAbility的啟動分為兩種情況:UIAbility冷啟動和UIAbility熱啟動。
UIAbility冷啟動:指的是UIAbility實例處于完全關(guān)閉狀態(tài)下被啟動逗载,這需要完整地加載和初始化UIAbility實例的代碼、資源等链烈。
UIAbility熱啟動:指的是UIAbility實例已經(jīng)啟動并在前臺運行過厉斟,由于某些原因切換到后臺,再次啟動該UIAbility實例强衡,這種情況下可以快速恢復(fù)UIAbility實例的狀態(tài)擦秽。
調(diào)用方UIAbility指定啟動頁面
調(diào)用方UIAbility啟動另外一個UIAbility時,通常需要跳轉(zhuǎn)到指定的頁面漩勤,此時需要在傳入的want參數(shù)中配置指定的頁面路徑信息感挥,可以通過want中的parameters參數(shù)增加一個自定義參數(shù)傳遞頁面跳轉(zhuǎn)信息。
import Want from '@ohos.app.ability.Want';
import common from '@ohos.app.ability.common';
import hilog from [<u>'@ohos.hilog';</u>](mailto:'@ohos.hilog';)
const TAG: string = 'IndexPage';
const DOMAIN_NUMBER: number = 0xFF00;
@Entry
@Component
struct Index {
build() {
Row() {
Column() {
Button('點擊')
.margin({
top: 20
})
.width('50%')
.height('5%')
.onClick(()=>{
// context為Ability對象的成員越败,在非Ability對象內(nèi)部調(diào)用需要
let context = getContext(this) as common.UIAbilityContext;
// 將Context對象傳遞過去
let wantInfo: Want = {
deviceId: '', // deviceId為空表示本設(shè)備
bundleName: 'com.loop.hello',
moduleName: 'entry', // moduleName非必選
abilityName: 'SecondAbility',
parameters: {
// 自定義參數(shù)傳遞頁面信息
router: 'SecondAnotherPage'
},
};
// context為調(diào)用方UIAbility的UIAbilityContext
context.startAbility(wantInfo).then(() => {
hilog.info(DOMAIN_NUMBER, TAG, 'startAbility success.');
}).catch((error) => {
hilog.error(DOMAIN_NUMBER, TAG, 'startAbility failed.');
});
})
}
.width('100%')
}
.height('100%')
}
}
目標(biāo)UIAbility冷啟動
目標(biāo)UIAbility冷啟動時触幼,在目標(biāo)UIAbility的onCreate()生命周期回調(diào)中,接收調(diào)用方傳過來的參數(shù)究飞。然后在目標(biāo)UIAbility的onWindowStageCreate()生命周期回調(diào)中置谦,解析EntryAbility傳遞過來的want參數(shù)堂鲤,獲取到需要加載的頁面信息url,傳入windowStage.loadContent()方法媒峡。
import UIAbility from '@ohos.app.ability.UIAbility';
import hilog from '@ohos.hilog';
import window from '@ohos.window';
import Want from '@ohos.app.ability.Want';
import AbilityConstant from '@ohos.app.ability.AbilityConstant';
import router from '@ohos.router';
const DOMAIN_NUMBER: number = 0xFF00;
const TAG: string = '[SecondAbility]';
export default class SecondAbility extends UIAbility {
secondAbilityWant : Want|undefined = undefined;
onCreate(want, launchParam) {
// 接收調(diào)用方UIAbility傳過來的參數(shù)]
this.secondAbilityWant = want;
}
onWindowStageCreate(windowStage: window.WindowStage) {
let url = 'pages/SecondPage';
if(this.secondAbilityWant?.parameters?.router && this.secondAbilityWant.parameters.router === 'SecondAnotherPage') {
url = 'pages/SecondAnotherPage';
}
windowStage.loadContent(url, (err, data) => {
if (err.code) {
hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
return;
}
hilog.info(0x0000, 'testTag', 'Succeeded in loading the content. Data: %{public}s', JSON.stringify(data) ?? '');
});
}
}
目標(biāo)UIAbility熱啟動
在應(yīng)用開發(fā)中瘟栖,會遇到目標(biāo)UIAbility實例之前已經(jīng)啟動過的場景,這時再次啟動目標(biāo)UIAbility時谅阿,不會重新走初始化邏輯半哟,只會直接觸發(fā)onNewWant()生命周期方法。為了實現(xiàn)跳轉(zhuǎn)到指定頁面签餐,需要在onNewWant()中解析參數(shù)進行處理镜沽。這里通過在SecondAbility的SecondAnotherPage啟動SecondAbility的SecondPage來模擬熱啟動。
import common from '@ohos.app.ability.common'
import Want from '@ohos.app.ability.Want'
import hilog from '@ohos.hilog'
const TAG: string = 'SecondAnotherPage';
const DOMAIN_NUMBER: number = 0xFF00;
@Entry
@Component
struct SecondAnotherPage {
@State message: string = 'SecondAnotherPage'
build() {
Row() {
Column() {
Text(this.message)
.fontSize(50)
.fontWeight(FontWeight.Bold)
.onClick(() => {
let context = getContext(this) as common.UIAbilityContext;
// 將Context對象傳遞過去
let wantInfo: Want = {
deviceId: '', // deviceId為空表示本設(shè)備
bundleName: 'com.loop.hello',
moduleName: 'entry', // moduleName非必選
abilityName: 'SecondAbility',
parameters: {
// 自定義信息
router: 'SecondPage'
},
};
// context為調(diào)用方UIAbility的UIAbilityContext
context.startAbility(wantInfo).then(()=>{
hilog.info(DOMAIN_NUMBER, TAG, 'startAbility success.');
}).catch((error) => {
hilog.error(DOMAIN_NUMBER, TAG, 'startAbility failed.');
});
})
}
.width('100%')
}
.height('100%')
}
}
在SecondAbility的onNewWant()回調(diào)中(而不會再走onCreate()和onWindowStageCreate()等初始化邏輯)解析調(diào)用方傳遞過來的want參數(shù)贱田,并進行指定頁面的跳轉(zhuǎn)缅茉。此時再次啟動該SecondAbility實例時,即可跳轉(zhuǎn)到該UIAbility實例的指定頁面男摧。
import UIAbility from '@ohos.app.ability.UIAbility';
import hilog from '@ohos.hilog';
import window from '@ohos.window';
import Want from '@ohos.app.ability.Want';
import AbilityConstant from '@ohos.app.ability.AbilityConstant';
import router from '@ohos.router';
const DOMAIN_NUMBER: number = 0xFF00;
const TAG: string = '[SecondAbility]';
export default class SecondAbility extends UIAbility {
secondAbilityWant : Want|undefined = undefined;
onCreate(want, launchParam) {
// 接收調(diào)用方UIAbility傳過來的參數(shù)]
this.secondAbilityWant = want;
}
onWindowStageCreate(windowStage: window.WindowStage) {
let url = 'pages/SecondPage';
if(this.secondAbilityWant?.parameters?.router && this.secondAbilityWant.parameters.router === 'SecondAnotherPage') {
url = 'pages/SecondAnotherPage';
}
windowStage.loadContent(url, (err, data) => {
if (err.code) {
hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
return;
}
hilog.info(0x0000, 'testTag', 'Succeeded in loading the content. Data: %{public}s', JSON.stringify(data) ?? '');
});
}
onNewWant(want: Want, launchParams: AbilityConstant.LaunchParam) {
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onNewWant');
if (want?.parameters?.router && want.parameters.router === 'SecondPage') {
let url = 'pages/SecondPage';
router.pushUrl({
url: url
}).catch((err) => {
hilog.error(DOMAIN_NUMBER, TAG, `Failed to push url. Code is ${err.code}, message is ${err.message}`);
});
}
}
}
當(dāng)被調(diào)用方UIAbility組件啟動模式設(shè)置為multiton啟動模式時蔬墩,每次啟動都會創(chuàng)建一個新的實例,那么onNewWant()回調(diào)就不會被用到耗拓。