UIAbility組件間交互(設(shè)備內(nèi))
UIAbility是系統(tǒng)調(diào)度的最小單元。在設(shè)備內(nèi)的功能模塊之間跳轉(zhuǎn)時,會涉及到啟動特定的UIAbility,該UIAbility可以是應(yīng)用內(nèi)的其他UIAbility,也可以是其他應(yīng)用的UIAbility(例如啟動三方支付UIAbility)。
本文將從如下場景分別介紹設(shè)備內(nèi)UIAbility間的交互方式。對于跨設(shè)備的應(yīng)用組件交互。
啟動應(yīng)用內(nèi)的UIAbility
當(dāng)一個應(yīng)用內(nèi)包含多個UIAbility時,存在應(yīng)用內(nèi)啟動UIAbility的場景。例如在支付應(yīng)用中從入口UIAbility啟動收付款UIAbility。
假設(shè)應(yīng)用中有兩個UIAbility:EntryAbility和FuncAbility(可以在同一個Module中,也可以在不同的Module中),需要從EntryAbility的頁面中啟動FuncAbility。
在EntryAbility中,通過調(diào)用[
startAbility()
]方法啟動UIAbility,[want]為UIAbility實例啟動的入口參數(shù),其中bundleName為待啟動應(yīng)用的Bundle名稱,abilityName為待啟動的Ability名稱,moduleName在待啟動的UIAbility屬于不同的Module時添加,parameters為自定義信息參數(shù)。示例中的context的獲取方式請參見[獲取UIAbility的上下文信息]。import common from '@ohos.app.ability.common'; import hilog from '@ohos.hilog'; import Want from '@ohos.app.ability.Want'; import { BusinessError } from '@ohos.base'; const TAG: string = '[Page_UIAbilityComponentsInteractive]'; const DOMAIN_NUMBER: number = 0xFF00; @Entry @Component struct Page_UIAbilityComponentsInteractive { private context = getContext(this) as common.UIAbilityContext; build() { Column() { //... List({ initialIndex: 0 }) { ListItem() { Row() { //... } .onClick(() = > { // context為Ability對象的成員,在非Ability對象內(nèi)部調(diào)用需要 // 將Context對象傳遞過去 let wantInfo: Want = { deviceId: '', // deviceId為空表示本設(shè)備 bundleName: 'com.samples.stagemodelabilitydevelop', moduleName: 'entry', // moduleName非必選 abilityName: 'FuncAbilityA', parameters: { // 自定義信息 info: '來自EntryAbility Page_UIAbilityComponentsInteractive頁面' }, }; // context為調(diào)用方UIAbility的UIAbilityContext this.context.startAbility(wantInfo).then(() = > { hilog.info(DOMAIN_NUMBER, TAG, 'startAbility success.'); }).catch((error: BusinessError) = > { hilog.error(DOMAIN_NUMBER, TAG, 'startAbility failed.'); }); }) } //... } //... } //... } }
在FuncAbility的[
onCreate()
]或者[onNewWant()
]生命周期回調(diào)文件中接收EntryAbility傳遞過來的參數(shù)。import type AbilityConstant from '@ohos.app.ability.AbilityConstant'; import UIAbility from '@ohos.app.ability.UIAbility'; import type Want from '@ohos.app.ability.Want'; export default class FuncAbilityA extends UIAbility { onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void { // 接收調(diào)用方UIAbility傳過來的參數(shù) let funcAbilityWant = want; let info = funcAbilityWant?.parameters?.info; } //... };
說明:
開發(fā)前請熟悉鴻蒙開發(fā)指導(dǎo)文檔 :[gitee.com/li-shizhen-skin/harmony-os/blob/master/README.md
]
在被拉起的FuncAbility中,可以通過獲取傳遞過來的want
參數(shù)的parameters
來獲取拉起方UIAbility的PID、Bundle Name等信息。在FuncAbility業(yè)務(wù)完成之后,如需要停止當(dāng)前UIAbility實例,在FuncAbility中通過調(diào)用[
terminateSelf()
]方法實現(xiàn)。import common from '@ohos.app.ability.common'; import hilog from '@ohos.hilog'; const TAG: string = '[Page_FromStageModel]'; const DOMAIN_NUMBER: number = 0xFF00; @Entry @Component struct Page_FromStageModel { build() { Column() { //... Button($r('app.string.FuncAbilityB')) .onClick(() = > { let context:common.UIAbilityContext = getContext(this) as common.UIAbilityContext; // UIAbilityContext // context為需要停止的UIAbility實例的AbilityContext context.terminateSelf((err) = > { if (err.code) { hilog.error(DOMAIN_NUMBER, TAG, `Failed to terminate self. Code is ${err.code}, message is ${err.message}`); return; } }); }) } //... } }
說明:
調(diào)用[
terminateSelf()
]方法停止當(dāng)前UIAbility實例時,默認(rèn)會保留該實例的快照(Snapshot),即在最近任務(wù)列表中仍然能查看到該實例對應(yīng)的任務(wù)。如不需要保留該實例的快照,可以在其對應(yīng)UIAbility的[module.json5配置文件]中,將[abilities標(biāo)簽]的removeMissionAfterTerminate字段配置為true。如需要關(guān)閉應(yīng)用所有的UIAbility實例,可以調(diào)用[ApplicationContext]的[
killAllProcesses()
]方法實現(xiàn)關(guān)閉應(yīng)用所有的進(jìn)程。
啟動應(yīng)用內(nèi)的UIAbility并獲取返回結(jié)果
在一個EntryAbility啟動另外一個FuncAbility時,希望在被啟動的FuncAbility完成相關(guān)業(yè)務(wù)后,能將結(jié)果返回給調(diào)用方。例如在應(yīng)用中將入口功能和帳號登錄功能分別設(shè)計為兩個獨立的UIAbility,在帳號登錄UIAbility中完成登錄操作后,需要將登錄的結(jié)果返回給入口UIAbility。
- 在EntryAbility中,調(diào)用[
startAbilityForResult()
]接口啟動FuncAbility,異步回調(diào)中的data用于接收FuncAbility停止自身后返回給EntryAbility的信息。示例中的context的獲取方式請參見[獲取UIAbility的上下文信息]。import common from '@ohos.app.ability.common'; import hilog from '@ohos.hilog'; import promptAction from '@ohos.promptAction'; import Want from '@ohos.app.ability.Want'; import { BusinessError } from '@ohos.base'; const TAG: string = '[Page_UIAbilityComponentsInteractive]'; const DOMAIN_NUMBER: number = 0xFF00; @Entry @Component struct Page_UIAbilityComponentsInteractive { build() { Column() { //... List({ initialIndex: 0 }) { ListItem() { Row() { //... } .onClick(() = > { let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; // UIAbilityContext const RESULT_CODE: number = 1001; let want: Want = { deviceId: '', // deviceId為空表示本設(shè)備 bundleName: 'com.samples.stagemodelabilitydevelop', moduleName: 'entry', // moduleName非必選 abilityName: 'FuncAbilityA', parameters: { // 自定義信息 info: '來自EntryAbility UIAbilityComponentsInteractive頁面' } }; context.startAbilityForResult(want).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, JSON.stringify(data.resultCode) ?? ''); }).catch((err: BusinessError) = > { hilog.error(DOMAIN_NUMBER, TAG, `Failed to start ability for result. Code is ${err.code}, message is ${err.message}`); }); }) } //... } //... } //... } }
- 在FuncAbility停止自身時,需要調(diào)用[
terminateSelfWithResult()
]方法,入?yún)bilityResult為FuncAbility需要返回給EntryAbility的信息。import common from '@ohos.app.ability.common'; import hilog from '@ohos.hilog'; const TAG: string = '[Page_FuncAbilityA]'; const DOMAIN_NUMBER: number = 0xFF00; @Entry @Component struct Page_FuncAbilityA { build() { Column() { //... List({ initialIndex: 0 }) { ListItem() { Row() { //... } .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.samples.stagemodelabilitydevelop', moduleName: 'entry', // moduleName非必選 abilityName: 'FuncAbilityB', parameters: { info: '來自FuncAbility Index頁面' }, }, }; 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; } }); }) } //... } //... } //... } }
- FuncAbility停止自身后,EntryAbility通過[
startAbilityForResult()
]方法回調(diào)接收被FuncAbility返回的信息,RESULT_CODE需要與前面的數(shù)值保持一致。import common from '@ohos.app.ability.common'; import hilog from '@ohos.hilog'; import promptAction from '@ohos.promptAction' import Want from '@ohos.app.ability.Want'; import { BusinessError } from '@ohos.base'; const TAG: string = '[Page_UIAbilityComponentsInteractive]'; const DOMAIN_NUMBER: number = 0xFF00; @Entry @Component struct Page_UIAbilityComponentsInteractive { build() { Column() { //... List({ initialIndex: 0 }) { ListItem() { Row() { //... } .onClick(() = > { let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; // UIAbilityContext const RESULT_CODE: number = 1001; let want: Want = { deviceId: '', // deviceId為空表示本設(shè)備 bundleName: 'com.samples.stagemodelabilitydevelop', moduleName: 'entry', // moduleName非必選 abilityName: 'FuncAbilityA', parameters: { // 自定義信息 info: '來自EntryAbility UIAbilityComponentsInteractive頁面' } }; context.startAbilityForResult(want).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, JSON.stringify(data.resultCode) ?? ''); }).catch((err: BusinessError) = > { hilog.error(DOMAIN_NUMBER, TAG, `Failed to start ability for result. Code is ${err.code}, message is ${err.message}`); }); }) } //... } //... } //... } }
啟動其他應(yīng)用的UIAbility
啟動其他應(yīng)用的UIAbility,通常用戶只需要完成一個通用的操作(例如需要選擇一個文檔應(yīng)用來查看某個文檔的內(nèi)容信息),推薦使用[隱式Want啟動]。系統(tǒng)會根據(jù)調(diào)用方的want參數(shù)來識別和啟動匹配到的應(yīng)用UIAbility。
啟動UIAbility有[顯式Want啟動和隱式Want啟動]兩種方式。
- 顯式Want啟動:啟動一個確定應(yīng)用的UIAbility,在want參數(shù)中需要設(shè)置該應(yīng)用bundleName和abilityName,當(dāng)需要拉起某個明確的UIAbility時,通常使用顯式Want啟動方式。
- 隱式Want啟動:根據(jù)匹配條件由用戶選擇啟動哪一個UIAbility,即不明確指出要啟動哪一個UIAbility(abilityName參數(shù)未設(shè)置),在調(diào)用[
startAbility()
]方法時,其入?yún)ant中指定了一系列的entities字段(表示目標(biāo)UIAbility額外的類別信息,如瀏覽器、視頻播放器)和actions字段(表示要執(zhí)行的通用操作,如查看、分享、應(yīng)用詳情等)等參數(shù)信息,然后由系統(tǒng)去分析want,并幫助找到合適的UIAbility來啟動。當(dāng)需要拉起其他應(yīng)用的UIAbility時,開發(fā)者通常不知道用戶設(shè)備中應(yīng)用的安裝情況,也無法確定目標(biāo)應(yīng)用的bundleName和abilityName,通常使用隱式Want啟動方式。
本文主要講解如何通過隱式Want啟動其他應(yīng)用的UIAbility。
- 將多個待匹配的文檔應(yīng)用安裝到設(shè)備,在其對應(yīng)UIAbility的[module.json5配置文件]中,配置skills標(biāo)簽的entities字段和actions字段。
{
"module": {
"abilities": [
{
...
"skills": [
{
"entities": [
...
"entity.system.default"
],
"actions": [
...
"ohos.want.action.viewData"
]
}
]
}
]
}
}
在調(diào)用方want參數(shù)中的entities和action需要被包含在待匹配UIAbility的skills配置的entities和actions中。系統(tǒng)匹配到符合entities和actions參數(shù)條件的UIAbility后,會彈出選擇框展示匹配到的UIAbility實例列表供用戶選擇使用。示例中的context的獲取方式請參見[獲取UIAbility的上下文信息]。
import common from '@ohos.app.ability.common'; import hilog from '@ohos.hilog'; import Want from '@ohos.app.ability.Want'; import { BusinessError } from '@ohos.base'; const TAG: string = '[Page_UIAbilityComponentsInteractive]'; const DOMAIN_NUMBER: number = 0xFF00; @Entry @Component struct Page_UIAbilityComponentsInteractive { build() { Column() { //... List({ initialIndex: 0 }) { ListItem() { Row() { //... } .onClick(() = > { let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; // UIAbilityContext let want: Want = { deviceId: '', // deviceId為空表示本設(shè)備 // uncomment line below if wish to implicitly query only in the specific bundle. // bundleName: 'com.samples.stagemodelabilityinteraction', action: 'ohos.want.action.viewData', // entities can be omitted. entities: ['entity.system.default'] }; // context為調(diào)用方UIAbility的UIAbilityContext context.startAbility(want).then(() = > { hilog.info(DOMAIN_NUMBER, TAG, 'Succeeded in starting FuncAbility.'); }).catch((err: BusinessError) = > { hilog.error(DOMAIN_NUMBER, TAG, `Failed to start FuncAbility. Code is ${err.code}, message is ${err.message}`); }); }) } //... } //... } //... } }
效果示意如下圖所示,點擊“打開PDF文檔”時,會彈出選擇框供用戶選擇。
在文檔應(yīng)用使用完成之后,如需要停止當(dāng)前UIAbility實例,通過調(diào)用[
terminateSelf()
]方法實現(xiàn)。import common from '@ohos.app.ability.common'; import hilog from '@ohos.hilog'; const DOMAIN_NUMBER: number = 0xFF00; const TAG: string = '[Page_FromStageModel]'; @Entry @Component struct Page_FromStageModel { build() { Column() { //... Button($r('app.string.FuncAbilityB')) .onClick(() = > { let context:common.UIAbilityContext = getContext(this) as common.UIAbilityContext; // UIAbilityContext // context為需要停止的UIAbility實例的AbilityContext context.terminateSelf((err) = > { if (err.code) { hilog.error(DOMAIN_NUMBER, TAG, `Failed to terminate self. Code is ${err.code}, message is ${err.message}`); return; } }); }) } //... } }
啟動其他應(yīng)用的UIAbility并獲取返回結(jié)果
當(dāng)使用隱式Want啟動其他應(yīng)用的UIAbility并希望獲取返回結(jié)果時,調(diào)用方需要使用[startAbilityForResult()
]方法啟動目標(biāo)UIAbility。例如主應(yīng)用中需要啟動三方支付并獲取支付結(jié)果。
- 在支付應(yīng)用對應(yīng)UIAbility的[module.json5配置文件]中,配置skills的entities字段和actions字段。
{ "module": { "abilities": [ { ... "skills": [ { "entities": [ ... "entity.system.default" ], "actions": [ ... "ohos.want.action.editData" ] } ] } ] } }
- 調(diào)用方使用[
startAbilityForResult()
]方法啟動支付應(yīng)用的UIAbility,在調(diào)用方want參數(shù)中的entities和action需要被包含在待匹配UIAbility的skills標(biāo)簽配置的entities和actions中。異步回調(diào)中的data用于后續(xù)接收支付UIAbility停止自身后返回給調(diào)用方的信息。系統(tǒng)匹配到符合entities和actions參數(shù)條件的UIAbility后,會彈出選擇框展示匹配到的UIAbility實例列表供用戶選擇使用。import common from '@ohos.app.ability.common'; import hilog from '@ohos.hilog'; import promptAction from '@ohos.promptAction' import Want from '@ohos.app.ability.Want'; import { BusinessError } from '@ohos.base'; const TAG: string = '[Page_UIAbilityComponentsInteractive]'; const DOMAIN_NUMBER: number = 0xFF00; @Entry @Component struct Page_UIAbilityComponentsInteractive { build() { Column() { //... List({ initialIndex: 0 }) { ListItem() { Row() { //... } .onClick(() = > { let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; // UIAbilityContext const RESULT_CODE: number = 1001; let want: Want = { deviceId: '', // deviceId為空表示本設(shè)備 bundleName: 'com.samples.stagemodelabilitydevelop', moduleName: 'entry', // moduleName非必選 abilityName: 'FuncAbilityA', parameters: { // 自定義信息 info: '來自EntryAbility UIAbilityComponentsInteractive頁面' } }; context.startAbilityForResult(want).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, JSON.stringify(data.resultCode) ?? ''); }).catch((err: BusinessError) = > { hilog.error(DOMAIN_NUMBER, TAG, `Failed to start ability for result. Code is ${err.code}, message is ${err.message}`); }); }) } //... } //... } //... } }
- 在支付UIAbility完成支付之后,需要調(diào)用[
terminateSelfWithResult()
]方法實現(xiàn)停止自身,并將abilityResult參數(shù)信息返回給調(diào)用方。import common from '@ohos.app.ability.common'; import hilog from '@ohos.hilog'; const TAG: string = '[Page_FuncAbilityA]'; const DOMAIN_NUMBER: number = 0xFF00; @Entry @Component struct Page_FuncAbilityA { build() { Column() { //... List({ initialIndex: 0 }) { ListItem() { Row() { //... } .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.samples.stagemodelabilitydevelop', moduleName: 'entry', // moduleName非必選 abilityName: 'FuncAbilityB', parameters: { info: '來自FuncAbility Index頁面' }, }, }; 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; } }); }) } //... } //... } //... } }
- 在調(diào)用方[
startAbilityForResult()
]方法回調(diào)中接收支付應(yīng)用返回的信息,RESULT_CODE需要與前面[terminateSelfWithResult()
]返回的數(shù)值保持一致。import common from '@ohos.app.ability.common'; import hilog from '@ohos.hilog'; import promptAction from '@ohos.promptAction'; import Want from '@ohos.app.ability.Want'; import { BusinessError } from '@ohos.base'; const TAG: string = '[Page_UIAbilityComponentsInteractive]'; const DOMAIN_NUMBER: number = 0xFF00; @Entry @Component struct Page_UIAbilityComponentsInteractive { build() { Column() { //... List({ initialIndex: 0 }) { ListItem() { Row() { //... } .onClick(() = > { let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; // UIAbilityContext const RESULT_CODE: number = 1001; let want: Want = { deviceId: '', // deviceId為空表示本設(shè)備 bundleName: 'com.samples.stagemodelabilitydevelop', moduleName: 'entry', // moduleName非必選 abilityName: 'FuncAbilityA', parameters: { // 自定義信息 info: '來自EntryAbility UIAbilityComponentsInteractive頁面' } }; context.startAbilityForResult(want).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, JSON.stringify(data.resultCode) ?? ''); }).catch((err: BusinessError) = > { hilog.error(DOMAIN_NUMBER, TAG, `Failed to start ability for result. Code is ${err.code}, message is ${err.message}`); }); }) } //... } //... } //... } }
啟動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)。
本文主要講解[目標(biāo)UIAbility冷啟動]和[目標(biāo)UIAbility熱啟動]兩種啟動指定頁面的場景,以及在講解啟動指定頁面之前會講解到在調(diào)用方如何指定啟動頁面。
調(diào)用方UIAbility指定啟動頁面
調(diào)用方UIAbility啟動另外一個UIAbility時,通常需要跳轉(zhuǎn)到指定的頁面。例如FuncAbility包含兩個頁面(Index對應(yīng)首頁,Second對應(yīng)功能A頁面),此時需要在傳入的want參數(shù)中配置指定的頁面路徑信息,可以通過want中的parameters參數(shù)增加一個自定義參數(shù)傳遞頁面跳轉(zhuǎn)信息。示例中的context的獲取方式請參見[獲取UIAbility的上下文信息]。
import common from '@ohos.app.ability.common';
import hilog from '@ohos.hilog';
import Want from '@ohos.app.ability.Want';
import { BusinessError } from '@ohos.base';
const TAG: string = '[Page_UIAbilityComponentsInteractive]';
const DOMAIN_NUMBER: number = 0xFF00;
@Entry
@Component
struct Page_UIAbilityComponentsInteractive {
build() {
Column() {
//...
List({ initialIndex: 0 }) {
ListItem() {
Row() {
//...
}
.onClick(() = > {
let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; // UIAbilityContext
let want: Want = {
deviceId: '', // deviceId為空表示本設(shè)備
bundleName: 'com.samples.stagemodelabilityinteraction',
moduleName: 'entry', // moduleName非必選
abilityName: 'FuncAbility',
parameters: { // 自定義參數(shù)傳遞頁面信息
router: 'FuncA'
}
};
// context為調(diào)用方UIAbility的UIAbilityContext
context.startAbility(want).then(() = > {
hilog.info(DOMAIN_NUMBER, TAG, 'Succeeded in starting ability.');
}).catch((err: BusinessError) = > {
hilog.error(DOMAIN_NUMBER, TAG, `Failed to start ability. Code is ${err.code}, message is ${err.message}`);
});
})
}
//...
}
//...
}
//...
}
}
目標(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 type AbilityConstant from '@ohos.app.ability.AbilityConstant';
import hilog from '@ohos.hilog';
import type Want from '@ohos.app.ability.Want';
import UIAbility from '@ohos.app.ability.UIAbility';
import type window from '@ohos.window';
import type { Router, UIContext } from '@ohos.arkui.UIContext';
import type { BusinessError } from '@ohos.base';
const DOMAIN_NUMBER: number = 0xFF00;
const TAG: string = '[EntryAbility]';
export default class EntryAbility extends UIAbility {
funcAbilityWant: Want | undefined = undefined;
uiContext: UIContext | undefined = undefined;
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
// 接收調(diào)用方UIAbility傳過來的參數(shù)
this.funcAbilityWant = want;
}
onWindowStageCreate(windowStage: window.WindowStage): void {
// Main window is created, set main page for this ability
hilog.info(DOMAIN_NUMBER, TAG, '%{public}s', 'Ability onWindowStageCreate');
// Main window is created, set main page for this ability
let url = 'pages/Index';
if (this.funcAbilityWant?.parameters?.router && this.funcAbilityWant.parameters.router === 'funcA') {
url = 'pages/Page_ColdStartUp';
}
windowStage.loadContent(url, (err, data) = > {
// ...
});
}
};
目標(biāo)UIAbility熱啟動
在應(yīng)用開發(fā)中,會遇到目標(biāo)UIAbility實例之前已經(jīng)啟動過的場景,這時再次啟動目標(biāo)UIAbility時,不會重新走初始化邏輯,只會直接觸發(fā)onNewWant()
生命周期方法。為了實現(xiàn)跳轉(zhuǎn)到指定頁面,需要在onNewWant()
中解析參數(shù)進(jìn)行處理。
例如短信應(yīng)用和聯(lián)系人應(yīng)用配合使用的場景。
- 用戶先打開短信應(yīng)用,短信應(yīng)用的UIAbility實例啟動,顯示短信應(yīng)用的主頁。
- 用戶將設(shè)備回到桌面界面,短信應(yīng)用進(jìn)入后臺運行狀態(tài)。
- 用戶打開聯(lián)系人應(yīng)用,找到聯(lián)系人張三。
- 用戶點擊聯(lián)系人張三的短信按鈕,會重新啟動短信應(yīng)用的UIAbility實例。
- 由于短信應(yīng)用的UIAbility實例已經(jīng)啟動過了,此時會觸發(fā)該UIAbility的
onNewWant()
回調(diào),而不會再走onCreate()
和onWindowStageCreate()
等初始化邏輯。
圖1 目標(biāo)UIAbility熱啟動
開發(fā)步驟如下所示。
- 冷啟動短信應(yīng)用的UIAbility實例時,在
onWindowStageCreate()
生命周期回調(diào)中,通過調(diào)用[getUIContext()
]接口獲取UI上下文實例[UIContext
]對象。import type AbilityConstant from '@ohos.app.ability.AbilityConstant'; import hilog from '@ohos.hilog'; import type Want from '@ohos.app.ability.Want'; import UIAbility from '@ohos.app.ability.UIAbility'; import type window from '@ohos.window'; import type { Router, UIContext } from '@ohos.arkui.UIContext'; import type { BusinessError } from '@ohos.base'; const DOMAIN_NUMBER: number = 0xFF00; const TAG: string = '[EntryAbility]'; export default class EntryAbility extends UIAbility { funcAbilityWant: Want | undefined = undefined; uiContext: UIContext | undefined = undefined; // ... onWindowStageCreate(windowStage: window.WindowStage): void { // Main window is created, set main page for this ability hilog.info(DOMAIN_NUMBER, TAG, '%{public}s', 'Ability onWindowStageCreate'); let url = 'pages/Index'; if (this.funcAbilityWant?.parameters?.router && this.funcAbilityWant.parameters.router === 'funcA') { url = 'pages/Page_ColdStartUp'; } windowStage.loadContent(url, (err, data) = > { if (err.code) { return; } let windowClass: window.Window; windowStage.getMainWindow((err, data) = > { if (err.code) { hilog.error(DOMAIN_NUMBER, TAG, `Failed to obtain the main window. Code is ${err.code}, message is ${err.message}`); return; } windowClass = data; this.uiContext = windowClass.getUIContext(); }); hilog.info(DOMAIN_NUMBER, TAG, 'Succeeded in loading the content. Data: %{public}s', JSON.stringify(data) ?? ''); }); } };
- 在短信應(yīng)用UIAbility的
onNewWant()
回調(diào)中解析調(diào)用方傳遞過來的want參數(shù),通過調(diào)用UIContext中的[getRouter()
]方法獲取[Router
]對象,并進(jìn)行指定頁面的跳轉(zhuǎn)。此時再次啟動該短信應(yīng)用的UIAbility實例時,即可跳轉(zhuǎn)到該短信應(yīng)用的UIAbility實例的指定頁面。import type AbilityConstant from '@ohos.app.ability.AbilityConstant'; import hilog from '@ohos.hilog'; import type Want from '@ohos.app.ability.Want'; import UIAbility from '@ohos.app.ability.UIAbility'; import type window from '@ohos.window'; import type { Router, UIContext } from '@ohos.arkui.UIContext'; import type { BusinessError } from '@ohos.base'; const DOMAIN_NUMBER: number = 0xFF00; const TAG: string = '[EntryAbility]'; export default class EntryAbility extends UIAbility { funcAbilityWant: Want | undefined = undefined; uiContext: UIContext | undefined = undefined; // ... onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void { if (want?.parameters?.router && want.parameters.router === 'funcB') { let funcAUrl = 'pages/Page_HotStartUp'; if (this.uiContext) { let router: Router = this.uiContext.getRouter(); router.pushUrl({ url: funcAUrl }).catch((err: BusinessError) = > { 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)就不會被用到。
啟動UIAbility指定窗口模式(僅對系統(tǒng)應(yīng)用開放)
當(dāng)用戶打開應(yīng)用時,應(yīng)用程序會以不同的窗口模式進(jìn)行展示,即啟動UIAbility的窗口模式。應(yīng)用程序可以啟動為全屏模式,懸浮窗模式或分屏模式。
全屏模式是指應(yīng)用程序啟動后,占據(jù)整個屏幕,用戶無法同時查看其他窗口或應(yīng)用程序。全屏模式通常適用于那些要求用戶專注于特定任務(wù)或界面的應(yīng)用程序。
懸浮窗模式是指應(yīng)用程序啟動后,以浮動窗口的形式顯示在屏幕上,用戶可以輕松切換到其他窗口或應(yīng)用程序。懸浮窗通常適用于需要用戶同時處理多個任務(wù)的應(yīng)用程序。
分屏模式允許用戶在同一屏幕上同時運行兩個應(yīng)用程序,其中一個應(yīng)用程序占據(jù)屏幕左側(cè)/上側(cè)的一部分,另一個應(yīng)用程序占據(jù)右側(cè)/下側(cè)的一部分。分屏模式主要用于提高用戶的多任務(wù)處理效率。
使用[startAbility()
]方法啟動UIAbility時,可以通過在入?yún)⒅性黾覽StartOptions]參數(shù)的windowMode屬性來配置啟動UIAbility的窗口模式。
說明:
- 如果在使用[
startAbility()
]方法啟動UIAbility時,入?yún)⒅形粗付╗StartOptions]參數(shù)的windowMode屬性,那么UIAbility將以系統(tǒng)默認(rèn)的窗口展示形態(tài)啟動。- 為了確保啟動的UIAbility展示形態(tài)能夠被支持,需要在該UIAbility對應(yīng)的[module.json5配置文件]中[abilities標(biāo)簽]的supportWindowMode字段確認(rèn)啟動的展示形態(tài)被支持。
以下是具體的操作步驟,以懸浮窗模式為例,假設(shè)需要從EntryAbility的頁面中啟動FuncAbility:
- 在調(diào)用[
startAbility()
]方法時,增加[StartOptions]參數(shù)。 - 在[StartOptions]參數(shù)中設(shè)置
windowMode
字段為WINDOW_MODE_FLOATING
,表示啟動的UIAbility將以懸浮窗的形式展示。 windowMode
屬性僅適用于系統(tǒng)應(yīng)用,三方應(yīng)用可以使用displayId
屬性。
示例中的context的獲取方式請參見[獲取UIAbility的上下文信息]。
import AbilityConstant from '@ohos.app.ability.AbilityConstant';
import common from '@ohos.app.ability.common';
import hilog from '@ohos.hilog';
import Want from '@ohos.app.ability.Want';
import { BusinessError } from '@ohos.base';
import StartOptions from '@ohos.app.ability.StartOptions';
const TAG: string = '[Page_UIAbilityComponentsInteractive]';
const DOMAIN_NUMBER: number = 0xFF00;
@Entry
@Component
struct Page_UIAbilityComponentsInteractive {
build() {
Column() {
//...
List({ initialIndex: 0 }) {
ListItem() {
Row() {
//...
}
.onClick(() = > {
let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; // UIAbilityContext
let want: Want = {
deviceId: '', // deviceId為空表示本設(shè)備
bundleName: 'com.samples.stagemodelabilitydevelop',
moduleName: 'entry', // moduleName非必選
abilityName: 'FuncAbilityB',
parameters: { // 自定義信息
info: '來自EntryAbility Index頁面'
}
};
let options: StartOptions = {
windowMode: AbilityConstant.WindowMode.WINDOW_MODE_FLOATING
};
// context為調(diào)用方UIAbility的UIAbilityContext
context.startAbility(want, options).then(() = > {
hilog.info(DOMAIN_NUMBER, TAG, 'Succeeded in starting ability.');
}).catch((err: BusinessError) = > {
hilog.error(DOMAIN_NUMBER, TAG, `Failed to start ability. Code is ${err.code}, message is ${err.message}`);
});
})
}
//...
}
//...
}
//...
}
}
效果示意如下圖所示。
通過Call調(diào)用實現(xiàn)UIAbility交互(僅對系統(tǒng)應(yīng)用開放)
Call調(diào)用是UIAbility能力的擴(kuò)展,它為UIAbility提供一種能夠被外部調(diào)用并與外部進(jìn)行通信的能力。Call調(diào)用支持前臺與后臺兩種啟動方式,使UIAbility既能被拉起到前臺展示UI,也可以在后臺被創(chuàng)建并運行。Call調(diào)用在調(diào)用方與被調(diào)用方間建立了IPC通信,因此應(yīng)用開發(fā)者可通過Call調(diào)用實現(xiàn)不同UIAbility之間的數(shù)據(jù)共享。
Call調(diào)用的核心接口是startAbilityByCall()
方法,與startAbility()
接口的不同之處在于:
- startAbilityByCall支持前臺與后臺兩種啟動方式,而
startAbility()
僅支持前臺啟動。 - 調(diào)用方可使用
startAbilityByCall()
所返回的Caller對象與被調(diào)用方進(jìn)行通信,而startAbility()
不具備通信能力。
Call調(diào)用的使用場景主要包括:
- 需要與被啟動的UIAbility進(jìn)行通信。
- 希望被啟動的UIAbility在后臺運行。
表1 Call調(diào)用相關(guān)名詞解釋
名詞 | 描述 |
---|---|
CallerAbility | 進(jìn)行Call調(diào)用的UIAbility(調(diào)用方)。 |
CalleeAbility | 被Call調(diào)用的UIAbility(被調(diào)用方)。 |
Caller | 實際對象,由startAbilityByCall接口返回,CallerAbility可使用Caller與CalleeAbility進(jìn)行通信。 |
Callee | 實際對象,被CalleeAbility持有,可與Caller進(jìn)行通信。 |
Call調(diào)用示意圖如下所示。
圖1 Call調(diào)用示意圖
- CallerAbility調(diào)用startAbilityByCall接口獲取Caller,并使用Caller對象的call方法向CalleeAbility發(fā)送數(shù)據(jù)。
- CalleeAbility持有一個Callee對象,通過Callee的on方法注冊回調(diào)函數(shù),當(dāng)接收到Caller發(fā)送的數(shù)據(jù)時將會調(diào)用對應(yīng)的回調(diào)函數(shù)。
說明:
- 當(dāng)前僅支持系統(tǒng)應(yīng)用使用Call調(diào)用。
- CalleeAbility的啟動模式需要為單實例。
- Call調(diào)用既支持本地(設(shè)備內(nèi))Call調(diào)用,也支持跨設(shè)備Call調(diào)用,下面介紹設(shè)備內(nèi)Call調(diào)用方法。跨設(shè)備Call調(diào)用方法請參見[跨設(shè)備Call調(diào)用]。
接口說明
Call功能主要接口如下表所示。具體的API詳見[接口文檔]。
表2 Call功能主要接口
接口名 | 描述 |
---|---|
startAbilityByCall(want: Want): Promise | 啟動指定UIAbility并獲取其Caller通信接口,默認(rèn)為后臺啟動,通過配置want可實現(xiàn)前臺啟動。AbilityContext與ServiceExtensionContext均支持該接口。 |
on(method: string, callback: CalleeCallBack): void | 通用組件Callee注冊method對應(yīng)的callback方法。 |
off(method: string): void | 通用組件Callee解注冊method的callback方法。 |
call(method: string, data: rpc.Parcelable): Promise | 向通用組件Callee發(fā)送約定序列化數(shù)據(jù)。 |
callWithResult(method: string, data: rpc.Parcelable): Promise | 向通用組件Callee發(fā)送約定序列化數(shù)據(jù), 并將Callee返回的約定序列化數(shù)據(jù)帶回。 |
release(): void | 釋放通用組件的Caller通信接口。 |
on(type: "release", callback: OnReleaseCallback): void | 注冊通用組件通信斷開監(jiān)聽通知。 |
設(shè)備內(nèi)通過Call調(diào)用實現(xiàn)UIAbility交互,涉及如下兩部分開發(fā):
- [創(chuàng)建Callee被調(diào)用端]
- [訪問Callee被調(diào)用端]
開發(fā)步驟(創(chuàng)建Callee被調(diào)用端)
在Callee被調(diào)用端,需要實現(xiàn)指定方法的數(shù)據(jù)接收回調(diào)函數(shù)、數(shù)據(jù)的序列化及反序列化方法。在需要接收數(shù)據(jù)期間,通過on接口注冊監(jiān)聽,無需接收數(shù)據(jù)時通過off接口解除監(jiān)聽。
- 配置UIAbility的啟動模式。
例如將CalleeAbility配置為單實例模式singleton
。 - 導(dǎo)入UIAbility模塊。
import UIAbility from '@ohos.app.ability.UIAbility';
- 定義約定的序列化數(shù)據(jù)。 調(diào)用端及被調(diào)用端發(fā)送接收的數(shù)據(jù)格式需協(xié)商一致,如下示例約定數(shù)據(jù)由number和string組成。
import type rpc from '@ohos.rpc'; class MyParcelable { num: number = 0; str: string = ''; constructor(num: number, string: string) { this.num = num; this.str = string; }; mySequenceable(num: number, string: string): void { this.num = num; this.str = string; }; marshalling(messageSequence: rpc.MessageSequence): boolean { messageSequence.writeInt(this.num); messageSequence.writeString(this.str); return true; }; unmarshalling(messageSequence: rpc.MessageSequence): boolean { this.num = messageSequence.readInt(); this.str = messageSequence.readString(); return true; }; }
- 實現(xiàn)Callee.on監(jiān)聽及Callee.off解除監(jiān)聽。
被調(diào)用端Callee的監(jiān)聽函數(shù)注冊時機(jī),取決于應(yīng)用開發(fā)者。注冊監(jiān)聽之前的數(shù)據(jù)不會被處理,取消監(jiān)聽之后的數(shù)據(jù)不會被處理。如下示例在UIAbility的onCreate注冊'MSG_SEND_METHOD'監(jiān)聽,在onDestroy取消監(jiān)聽,收到序列化數(shù)據(jù)后作相應(yīng)處理并返回,應(yīng)用開發(fā)者根據(jù)實際需要做相應(yīng)處理。具體示例代碼如下:import type AbilityConstant from '@ohos.app.ability.AbilityConstant'; import UIAbility from '@ohos.app.ability.UIAbility'; import type Want from '@ohos.app.ability.Want'; import hilog from '@ohos.hilog'; import type rpc from '@ohos.rpc'; import type { Caller } from '@ohos.app.ability.UIAbility'; const MSG_SEND_METHOD: string = 'CallSendMsg'; const DOMAIN_NUMBER: number = 0xFF00; const TAG: string = '[CalleeAbility]'; class MyParcelable { num: number = 0; str: string = ''; constructor(num: number, string: string) { this.num = num; this.str = string; }; mySequenceable(num: number, string: string): void { this.num = num; this.str = string; }; marshalling(messageSequence: rpc.MessageSequence): boolean { messageSequence.writeInt(this.num); messageSequence.writeString(this.str); return true; }; unmarshalling(messageSequence: rpc.MessageSequence): boolean { this.num = messageSequence.readInt(); this.str = messageSequence.readString(); return true; }; }; function sendMsgCallback(data: rpc.MessageSequence): rpc.Parcelable { hilog.info(DOMAIN_NUMBER, TAG, '%{public}s', 'CalleeSortFunc called'); // 獲取Caller發(fā)送的序列化數(shù)據(jù) let receivedData: MyParcelable = new MyParcelable(0, ''); data.readParcelable(receivedData); hilog.info(DOMAIN_NUMBER, TAG, '%{public}s', `receiveData[${receivedData.num}, ${receivedData.str}]`); let num: number = receivedData.num; // 作相應(yīng)處理 // 返回序列化數(shù)據(jù)result給Caller return new MyParcelable(num + 1, `send ${receivedData.str} succeed`) as rpc.Parcelable; }; export default class CalleeAbility extends UIAbility { caller: Caller | undefined; onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void { try { this.callee.on(MSG_SEND_METHOD, sendMsgCallback); } catch (error) { hilog.error(DOMAIN_NUMBER, TAG, '%{public}s', `Failed to register. Error is ${error}`); }; }; releaseCall(): void { try { if (this.caller) { this.caller.release(); this.caller = undefined; } hilog.info(DOMAIN_NUMBER, TAG, '%{public}s', 'caller release succeed'); } catch (error) { hilog.info(DOMAIN_NUMBER, TAG, '%{public}s', `caller release failed with ${error}`); }; }; onDestroy(): void { try { this.callee.off(MSG_SEND_METHOD); hilog.info(DOMAIN_NUMBER, TAG, '%{public}s', 'Callee OnDestroy'); this.releaseCall(); } catch (error) { hilog.error(DOMAIN_NUMBER, TAG, '%{public}s', `Failed to register. Error is ${error}`); }; }; };
開發(fā)步驟(訪問Callee被調(diào)用端)
- 導(dǎo)入UIAbility模塊。
import UIAbility from '@ohos.app.ability.UIAbility';
- 獲取Caller通信接口。 UIAbilityContext屬性實現(xiàn)了startAbilityByCall方法,用于獲取指定通用組件的Caller通信接口。如下示例通過this.context獲取UIAbility實例的context屬性,使用startAbilityByCall拉起Callee被調(diào)用端并獲取Caller通信接口,注冊Caller的onRelease監(jiān)聽。應(yīng)用開發(fā)者根據(jù)實際需要做相應(yīng)處理。
`HarmonyOS與OpenHarmony鴻蒙文檔籽料:mau123789是v直接拿`
import AbilityConstant from '@ohos.app.ability.AbilityConstant';
import common from '@ohos.app.ability.common';
import hilog from '@ohos.hilog';
import promptAction from '@ohos.promptAction'
import Want from '@ohos.app.ability.Want';
import StartOptions from '@ohos.app.ability.StartOptions';
import { BusinessError } from '@ohos.base';
import { Caller } from '@ohos.app.ability.UIAbility';
const TAG: string = '[Page_UIAbilityComponentsInteractive]';
const DOMAIN_NUMBER: number = 0xFF00;
@Entry
@Component
struct Page_UIAbilityComponentsInteractive {
caller: Caller | undefined = undefined;
// 注冊caller的release監(jiān)聽
private regOnRelease(caller: Caller): void {
hilog.info(DOMAIN_NUMBER, TAG, `caller is ${caller}`);
try {
caller.on('release', (msg: string) = > {
hilog.info(DOMAIN_NUMBER, TAG, `caller onRelease is called ${msg}`);
})
hilog.info(DOMAIN_NUMBER, TAG, 'succeeded in registering on release.');
} catch (err) {
let code = (err as BusinessError).code;
let message = (err as BusinessError).message;
hilog.error(DOMAIN_NUMBER, TAG, `Failed to caller register on release. Code is ${code}, message is ${message}`);
}
};
build() {
Column() {
// ...
List({ initialIndex: 0 }) {
// ...
ListItem() {
Row() {
// ...
}
.onClick(() = > {
let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; // UIAbilityContext
let want: Want = {
bundleName: 'com.samples.stagemodelabilityinteraction',
abilityName: 'CalleeAbility',
parameters: { // 自定義信息
info: 'CallSendMsg'
}
};
context.startAbilityByCall(want).then((caller: Caller) = > {
hilog.info(DOMAIN_NUMBER, TAG, `Succeeded in starting ability.Code is ${caller}`);
if (caller === undefined) {
hilog.info(DOMAIN_NUMBER, TAG, 'get caller failed');
return;
}
else {
hilog.info(DOMAIN_NUMBER, TAG, 'get caller success');
this.regOnRelease(caller);
promptAction.showToast({
message: $r('app.string.CallerSuccess')
});
try {
caller.release();
} catch (releaseErr) {
console.log('Caller.release catch error, error.code: ' + JSON.stringify(releaseErr.code) +
' error.message: ' + JSON.stringify(releaseErr.message));
}
}
}).catch((err: BusinessError) = > {
hilog.error(DOMAIN_NUMBER, TAG, `Failed to start ability. Code is ${err.code}, message is ${err.message}`);
});
})
}
// ...
}
// ...
}
// ...
}
}
審核編輯 黃宇
-
框架
+關(guān)注
關(guān)注
0文章
403瀏覽量
17488 -
組件
+關(guān)注
關(guān)注
1文章
512瀏覽量
17827 -
鴻蒙
+關(guān)注
關(guān)注
57文章
2352瀏覽量
42858
發(fā)布評論請先 登錄
相關(guān)推薦
評論