2024-09-18

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叠国,多個頁面的方式。


單UIAbility應(yīng)用

如果開發(fā)者希望在任務(wù)視圖中看到多個任務(wù)戴尸,或者需要同時開啟多個窗口粟焊,則建議使用多個UIAbility開發(fā)不同的模塊功能。


多UIAbility應(yīng)用

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)就不會被用到耗拓。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末拇颅,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子乔询,更是在濱河造成了極大的恐慌樟插,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,525評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件竿刁,死亡現(xiàn)場離奇詭異黄锤,居然都是意外死亡,警方通過查閱死者的電腦和手機食拜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,203評論 3 395
  • 文/潘曉璐 我一進店門鸵熟,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人负甸,你說我怎么就攤上這事流强。” “怎么了呻待?”我有些...
    開封第一講書人閱讀 164,862評論 0 354
  • 文/不壞的土叔 我叫張陵打月,是天一觀的道長。 經(jīng)常有香客問我蚕捉,道長奏篙,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,728評論 1 294
  • 正文 為了忘掉前任鱼冀,我火速辦了婚禮报破,結(jié)果婚禮上悠就,老公的妹妹穿的比我還像新娘。我一直安慰自己充易,他們只是感情好梗脾,可當(dāng)我...
    茶點故事閱讀 67,743評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著盹靴,像睡著了一般炸茧。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上稿静,一...
    開封第一講書人閱讀 51,590評論 1 305
  • 那天梭冠,我揣著相機與錄音,去河邊找鬼改备。 笑死控漠,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的悬钳。 我是一名探鬼主播盐捷,決...
    沈念sama閱讀 40,330評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼默勾!你這毒婦竟也來了碉渡?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,244評論 0 276
  • 序言:老撾萬榮一對情侶失蹤母剥,失蹤者是張志新(化名)和其女友劉穎滞诺,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體环疼,經(jīng)...
    沈念sama閱讀 45,693評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡习霹,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,885評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了秦爆。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片序愚。...
    茶點故事閱讀 40,001評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖等限,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情芬膝,我是刑警寧澤望门,帶...
    沈念sama閱讀 35,723評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站锰霜,受9級特大地震影響筹误,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜癣缅,卻給世界環(huán)境...
    茶點故事閱讀 41,343評論 3 330
  • 文/蒙蒙 一厨剪、第九天 我趴在偏房一處隱蔽的房頂上張望哄酝。 院中可真熱鬧,春花似錦祷膳、人聲如沸陶衅。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,919評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽搀军。三九已至,卻和暖如春勇皇,著一層夾襖步出監(jiān)牢的瞬間罩句,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,042評論 1 270
  • 我被黑心中介騙來泰國打工敛摘, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留门烂,地道東北人。 一個月前我還...
    沈念sama閱讀 48,191評論 3 370
  • 正文 我出身青樓兄淫,卻偏偏與公主長得像屯远,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子拖叙,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,955評論 2 355

推薦閱讀更多精彩內(nèi)容