鴻蒙HarmonyOS實戰-Stage模型(UIAbility元件)

蜀道山QAQ發表於2024-05-14

🚀一、UIAbility元件

🔎1.概述

HarmonyOS中的Stage模型是一種基於UIAbility元件的應用程式架構。UIAbility是HarmonyOS系統中用於構建使用者介面的基本元件之一。它負責處理應用程式介面的顯示和互動。

在Stage模型中,每個應用程式都有一個或多個Stage。Stage是一個獨立的介面容器,可以包含多個UIAbility。每個UIAbility代表了應用程式中的一個介面頁面,可以獨立展示、互動和管理資料。

UIAbility可以實現不同的功能,比如展示靜態資訊、接受使用者輸入、進行資料處理等。它們可以被動態新增或移除,實現動態的介面切換和功能擴充套件。

透過Stage模型,應用程式可以根據不同的場景和需求,靈活地組織和管理UIAbility。UIAbility之間可以透過事件和資料進行通訊,實現介面間的互動和資料傳遞。

{
  "module": {
    // ...
    "abilities": [
      {
        "name": "EntryAbility", // UIAbility元件的名稱
        "srcEntrance": "./ets/entryability/EntryAbility.ts", // UIAbility元件的程式碼路徑
        "description": "$string:EntryAbility_desc", // UIAbility元件的描述資訊
        "icon": "$media:icon", // UIAbility元件的圖示
        "label": "$string:EntryAbility_label", // UIAbility元件的標籤
        "startWindowIcon": "$media:icon", // UIAbility元件啟動頁面圖示資原始檔的索引
        "startWindowBackground": "$color:start_window_background", // UIAbility元件啟動頁面背景顏色資原始檔的索引
        // ...
      }
    ]
  }
}

image

🔎2.生命週期

在HarmonyOS中,Stage模型指的是應用程式的頁面層級結構管理方式,透過Stage模型可以實現頁面的切換和管理。UIAbility元件是在Stage模型中管理的頁面元件,它具備自己的生命週期。

更多鴻蒙最新技術知識點,請關注作者部落格:https://t.doruo.cn/14DjR1rEY

UIAbility的生命週期包括Create、Foreground、Background、Destroy四個狀態,如下圖所示。

image

🦋2.1 生命週期狀態說明

☀️2.1.1 Create狀態

在HarmonyOS中,Stage模型是一種用於構建應用程式的UI模型,它由多個UIAbility元件組成。每個UIAbility元件都有自己的生命週期函式,其中包括Create函式。

Create函式是在UIAbility元件被建立時呼叫的函式。在這個函式中,我們可以進行一些初始化操作,例如建立UI介面資源、繫結事件監聽器等。

import UIAbility from '@ohos.app.ability.UIAbility';
import Window from '@ohos.window';

export default class EntryAbility extends UIAbility {
    onCreate(want, launchParam) {
        // 應用初始化
    }
    // ...
}

image

☀️2.1.2 WindowStageCreate和WindowStageDestroy狀態

WindowStageCreate和WindowStageDestroy是用於建立和銷燬視窗階段的方法。

WindowStageCreate方法用於建立一個新的視窗階段,並返回該階段的控制代碼。透過該控制代碼,可以對視窗階段進行操作,如設定視窗的大小、位置、背景等。

WindowStageDestroy方法用於銷燬一個視窗階段,並釋放相關的資源。在呼叫該方法之前,需要先解除與視窗階段相關的所有繫結,以確保資源能夠被正確釋放。

image

import UIAbility from '@ohos.app.ability.UIAbility';
import Window from '@ohos.window';

export default class EntryAbility extends UIAbility {
    onWindowStageCreate(windowStage: Window.WindowStage) {
        // 設定WindowStage的事件訂閱(獲焦/失焦、可見/不可見)

        // 設定UI介面載入
        windowStage.loadContent('pages/Index', (err, data) => {
            // ...
        });
    }
    onWindowStageDestroy() {
        // 釋放UI介面資源
    }
}

image

☀️2.1.3 Foreground和Background狀態

應用程式可以處於Foreground(前臺)或Background(後臺)狀態。

Foreground狀態是指應用程式當前處於使用者正在與之互動的狀態,即應用程式在螢幕前面顯示並佔用使用者焦點。在Foreground狀態下,應用程式可以接收使用者的輸入事件,並在螢幕上顯示相關的介面。

Background狀態是指應用程式當前不可見,即應用程式在後臺執行並不佔用使用者焦點。在Background狀態下,應用程式仍然可以繼續執行各種任務,如處理網路請求、計算等。然而,由於應用程式不可見,因此無法直接與使用者進行互動。

HarmonyOS提供了一些機制來管理應用程式的Foreground和Background狀態。例如,應用程式可以透過呼叫API來請求將自己移至前臺或後臺,或者可以透過註冊生命週期回撥來獲取自己的狀態變化通知。此外,HarmonyOS還提供了後臺工作管理員(BackgroundTaskManager)來管理應用程式在後臺執行的任務,以確保後臺任務的效能和穩定性。

import UIAbility from '@ohos.app.ability.UIAbility';

export default class EntryAbility extends UIAbility {
    onForeground() {
        // 申請系統需要的資源,或者重新申請在onBackground中釋放的資源
    }

    onBackground() {
        // 釋放UI介面不可見時無用的資源,或者在此回撥中執行較為耗時的操作
        // 例如狀態儲存等
    }
}

image

☀️2.1.4 Destroy狀態

Destroy狀態是指應用程式已經被完全銷燬的狀態。當應用程式處於Destroy狀態時,它的所有資源都被釋放,沒有任何活動在進行。這意味著應用程式不再存在於系統的活動棧中,並且無法透過返回鍵或其他方式重新進入。

當應用程式處於Destroy狀態時,系統可能會回收應用程式的記憶體和其他資源,以便為其他應用程式或系統提供更多資源。在Destroy狀態下,應用程式的生命週期已經結束,並且它不再對使用者可見。

更多鴻蒙最新技術知識點,請關注作者部落格:https://t.doruo.cn/14DjR1rEY

Destroy狀態通常在以下情況下發生:

  • 使用者手動關閉應用程式;
  • 系統資源不足,需要回收應用程式的資源;
  • 應用程式因為異常或錯誤而被系統強制銷燬。

在HarmonyOS中,應用程式的Destroy狀態與其他狀態(如Active、Inactive和Paused)相互轉換,組成了完整的應用程式生命週期。

import UIAbility from '@ohos.app.ability.UIAbility';
import Window from '@ohos.window';

export default class EntryAbility extends UIAbility {
    onDestroy() {
        // 系統資源的釋放、資料的儲存等
    }
}

image

🔎3.啟動模式

🦋3.1 singleton(單例項模式)

單例模式是一種建立只允許存在一個例項的設計模式。在單例模式中,一個類只能建立一個物件,並且提供一個訪問該物件的全域性訪問點。

使用單例模式的主要目的是確保在整個應用程式中只有一個例項,這樣可以節省系統資源,並且可以方便地控制對該例項的訪問。

單例模式通常採用以下方式實現:

  1. 私有化建構函式:將類的建構函式設定為私有,使得其他類無法直接建立該類的例項。
  2. 提供一個靜態方法或屬性來獲取例項:由於無法直接建立例項,需要提供一個靜態方法或屬性來獲取類的唯一例項。該方法或屬性會在第一次呼叫時建立例項,並在以後的呼叫中返回同一個例項。
  3. 執行緒安全:由於多執行緒環境下,可能會出現多個執行緒同時呼叫獲取例項的方法,並嘗試建立例項的情況。為了確保執行緒安全,可以使用同步鎖或雙重檢查鎖定機制來保證只有一個執行緒能夠建立例項。

使用單例模式時需要注意以下幾點:

  1. 單例模式會引入全域性狀態,可能會增加系統的複雜性和耦合度。
  2. 單例模式不適合有多個例項場景的情況。
  3. 單例模式可能會導致單例例項的記憶體洩漏問題,因為該例項在整個應用程式的生命週期中保持存在。
  4. 單例模式可能會影響程式碼的可測試性,因為單例例項會在測試中被共享,可能會導致測試結果受到其他測試用例的影響。

在HarmonyOS中每次呼叫startAbility()方法時,如果應用程序中該型別的UIAbility例項已經存在,則複用系統中的UIAbility例項。系統中只存在唯一一個該UIAbility例項,即在最近任務列表中只存在一個該型別的UIAbility例項。

單例模式配置如下:

{
  "module": {
    // ...
    "abilities": [
      {
        "launchType": "singleton",
        // ...
      }
    ]
  }
}

image

🦋3.2 multiton(多例項模式)

Multiton(多例項模式)是設計模式中的一種,它是單例模式的一種變體。在單例模式中,一個類只能有一個例項;而在多例項模式中,一個類可以擁有多個例項,每個例項有不同的識別符號或鍵值。每個例項都可以被獨立訪問,類似於一個字典結構,可以根據不同的鍵值獲取對應的例項。

Multiton模式通常用於需要建立一組有限且確定數量的類例項的場景。每個例項都有自己的獨立狀態和行為,但是可以透過共享某些公共資源進行協作。Multiton模式可以提供更大的靈活性和可擴充套件性,同時保持每個例項的獨立性。

Multiton模式可以透過使用一個字典或者對映結構來維護每個例項的集合,並透過一個鍵值來唯一標識每個例項。當需要獲取一個例項時,可以根據提供的鍵值從集合中獲取對應的例項。如果集合中不存在對應的例項,則可以建立一個新的例項並新增到集合中。

Multiton模式的優點包括可以靈活地管理和訪問多個例項,提供更細粒度的控制,同時每個例項都是獨立的,可以有自己獨立的狀態和行為。但是Multiton模式也存在一些缺點,例如需要額外的管理和維護工作,同時可能會導致增加記憶體消耗。因此,在使用Multiton模式時需要根據具體的需求和場景來進行權衡和選擇。

多例項模式配置如下:

{
  "module": {
    // ...
    "abilities": [
      {
        "launchType": "multiton",
        // ...
      }
    ]
  }
}

image

🦋3.3 specified(指定例項模式)

指定例項模式(specified instance pattern)用於限定一個類只能有固定數目的例項。在這種模式中,類的例項被限制為預先定義的數量,且這些例項在執行時不能被建立或銷燬。

指定例項模式常用於需要限制某個類的例項數量的場景。例如,一個資料庫連線池的實現可以使用指定例項模式來限制連線的數量,確保不會建立過多的連線。另一個例子是執行緒池,可以使用指定例項模式來限制執行緒的數量。

在指定例項模式中,通常會維護一個內部的例項列表,來跟蹤已經建立的例項。當需要獲取一個例項時,會首先檢查這個列表,如果列表中已經有足夠的例項,則直接返回一個例項給呼叫者。如果列表中沒有足夠的例項,則根據需求建立新的例項,並新增到列表中。當例項不再需要時,通常會將其從列表中移除,以便可以繼續建立新的例項。

指定例項模式可以有效地控制類的例項數量,避免了資源的浪費和過度建立例項的問題。然而,由於例項數量是固定的,所以可能會出現競爭條件和資源瓶頸的問題,需要進行合理的設計和調優。

更多鴻蒙最新技術知識點,請關注作者部落格:https://t.doruo.cn/14DjR1rEY

指定例項模式配置如下:

{
  "module": {
    // ...
    "abilities": [
      {
        "launchType": "specified",
        // ...
      }
    ]
  }
}

image

案例說明:

例如有兩個UIAbility:EntryAbility和FuncAbility,FuncAbility配置為specified啟動模式,需要從EntryAbility的頁面中啟動FuncAbility。

1、在FuncAbility中,將module.json5配置檔案的"launchType"欄位配置為"specified"。

{
  "module": {
    // ...
    "abilities": [
      {
        "launchType": "specified",
        // ...
      }
    ]
  }
}

2、在EntryAbility中,呼叫startAbility()方法時,在want引數中,增加一個自定義引數來區別UIAbility例項,例如增加一個"instanceKey"自定義引數。

// 在啟動指定例項模式的UIAbility時,給每一個UIAbility例項配置一個獨立的Key標識
// 例如在文件使用場景中,可以用文件路徑作為Key標識
function getInstance() {
    // ...
}

let want = {
    deviceId: '', // deviceId為空表示本裝置
    bundleName: 'com.example.myapplication',
    abilityName: 'FuncAbility',
    moduleName: 'module1', // moduleName非必選
    parameters: { // 自定義資訊
        instanceKey: getInstance(),
    },
}
// context為呼叫方UIAbility的AbilityContext
this.context.startAbility(want).then(() => {
    // ...
}).catch((err) => {
    // ...
})

3、獲取UIAbility引數

import AbilityStage from '@ohos.app.ability.AbilityStage';

export default class MyAbilityStage extends AbilityStage {
    onAcceptWant(want): string {
        // 在被呼叫方的AbilityStage中,針對啟動模式為specified的UIAbility返回一個UIAbility例項對應的一個Key值
        // 當前示例指的是module1 Module的FuncAbility
        if (want.abilityName === 'FuncAbility') {
            // 返回的字串Key標識為自定義拼接的字串內容
            return `ControlModule_EntryAbilityInstance_${want.parameters.instanceKey}`;
        }

        return '';
    }
}

🔎4.基本用法

🦋4.1 指定UIAbility的啟動頁面

import UIAbility from '@ohos.app.ability.UIAbility';
import Window from '@ohos.window';

export default class EntryAbility extends UIAbility {
    onWindowStageCreate(windowStage: Window.WindowStage) {
        // Main window is created, set main page for this ability
        windowStage.loadContent('pages/Index', (err, data) => {
            // ...
        });
    }

    // ...
}

image

🦋4.2 獲取UIAbility的上下文資訊

透過UIAbilityContext可以獲取UIAbility的相關配置資訊,包括包程式碼路徑、Bundle名稱、Ability名稱和應用程式需要的環境狀態等屬性資訊。此外,還可以使用UIAbilityContext提供的方法來操作UIAbility例項,例如startAbility()、connectServiceExtensionAbility()、terminateSelf()等。

import UIAbility from '@ohos.app.ability.UIAbility';

export default class EntryAbility extends UIAbility {
    onCreate(want, launchParam) {
        // 獲取UIAbility例項的上下文
        let context = this.context;

        // ...
    }
}

image

在頁面獲取

import common from '@ohos.app.ability.common';

@Entry
@Component
struct Index {
  private context = getContext(this) as common.UIAbilityContext;

  startAbilityTest() {
    let want = {
      // Want引數資訊
    };
    this.context.startAbility(want);
  }

  // 頁面展示
  build() {
    // ...
  }
}

🔎5.UIAbility元件與UI的資料同步

主要有兩種方式:
image

🦋5.1 使用EventHub進行資料通訊

1、註冊事件,使用其中一種即可

import UIAbility from '@ohos.app.ability.UIAbility';

const TAG: string = '[Example].[Entry].[EntryAbility]';

export default class EntryAbility extends UIAbility {
    func1(...data) {
        // 觸發事件,完成相應的業務操作
        console.info(TAG, '1. ' + JSON.stringify(data));
    }

    onCreate(want, launch) {
        // 獲取eventHub
        let eventhub = this.context.eventHub;
        // 執行訂閱操作
        eventhub.on('event1', this.func1);
        eventhub.on('event1', (...data) => {
            // 觸發事件,完成相應的業務操作
            console.info(TAG, '2. ' + JSON.stringify(data));
        });
    }
}

image

2、觸發事件

import common from '@ohos.app.ability.common';

@Entry
@Component
struct Index {
  private context = getContext(this) as common.UIAbilityContext;

  eventHubFunc() {
    // 不帶引數觸發自定義“event1”事件
    this.context.eventHub.emit('event1');
    // 帶1個引數觸發自定義“event1”事件
    this.context.eventHub.emit('event1', 1);
    // 帶2個引數觸發自定義“event1”事件
    this.context.eventHub.emit('event1', 2, 'test');
    // 開發者可以根據實際的業務場景設計事件傳遞的引數
  }

  // 頁面展示
  build() {
    // ...
  }
}

3、取消事件

// context為UIAbility例項的AbilityContext
this.context.eventHub.off('event1');
🦋5.2 使用globalThis進行資料同步

在HarmonyOS中,globalThis是一個全域性物件,它提供了一個統一的方式來訪問不同環境下的全域性物件。在HarmonyOS中,globalThis可以用來訪問當前執行環境中的全域性物件,可以是瀏覽器環境中的window物件,也可以是Node.js環境中的global物件。

使用globalThis可以方便地在不同環境下編寫通用的程式碼,不需要針對不同的環境做特殊的處理。例如,可以使用globalThis來訪問全域性的console物件,無論在瀏覽器環境還是Node.js環境中,都可以使用console.log()來輸出日誌。

更多鴻蒙最新技術知識點,請關注作者部落格:https://t.doruo.cn/14DjR1rEY

雖然globalThis提供了一種通用的訪問全域性物件的方式,但在實際程式設計中還是建議根據具體的環境來使用相應的全域性物件。

image

☀️5.2.1 UIAbility和Page之間使用globalThis

1、註冊

import UIAbility from '@ohos.app.ability.UIAbility'

export default class EntryAbility extends UIAbility {
    onCreate(want, launch) {
        globalThis.entryAbilityWant = want;
        // ...
    }

    // ...
}

2、獲取

let entryAbilityWant;

@Entry
@Component
struct Index {
  aboutToAppear() {
    entryAbilityWant = globalThis.entryAbilityWant;
  }

  // 頁面展示
  build() {
    // ...
  }
}
☀️5.2.2 UIAbility和UIAbility之間使用globalThis

1、註冊

import UIAbility from '@ohos.app.ability.UIAbility'

export default class AbilityA extends UIAbility {
    onCreate(want, launch) {
        globalThis.entryAbilityStr = 'AbilityA'; // AbilityA存放字串“AbilityA”到globalThis
        // ...
    }
}

2、獲取

import UIAbility from '@ohos.app.ability.UIAbility'

export default class AbilityB extends UIAbility {
    onCreate(want, launch) {
        // AbilityB從globalThis讀取name並輸出
        console.info('name from entryAbilityStr: ' + globalThis.entryAbilityStr);
        // ...
    }
}
☀️5.2.3 globalThis使用的注意事項

image

案例:

1、在AbilityA檔案中使用globalThis中存放了UIAbilityContext。

import UIAbility from '@ohos.app.ability.UIAbility'

export default class AbilityA extends UIAbility {
    onCreate(want, launch) {
        globalThis.context = this.context; // AbilityA存放context到globalThis
        // ...
    }
}

2、在AbilityA的頁面中獲取該UIAbilityContext並進行使用。使用完成後將AbilityA例項切換至後臺。

@Entry
@Component
struct Index {
  onPageShow() {
    let ctx = globalThis.context; // 頁面中從globalThis中取出context並使用
    let permissions = ['com.example.permission']
    ctx.requestPermissionsFromUser(permissions,(result) => {
       // ...
    });
  }
  // 頁面展示
  build() {
    // ...
  }
}

3、在AbilityB檔案中使用globalThis中存放了UIAbilityContext,並且命名為相同的名稱。

import UIAbility from '@ohos.app.ability.UIAbility'

export default class AbilityB extends UIAbility {
    onCreate(want, launch) {
        // AbilityB覆蓋了AbilityA在globalThis中存放的context
        globalThis.context = this.context;
        // ...
    }
}

4、在AbilityB的頁面中獲取該UIAbilityContext並進行使用。此時獲取到的globalThis.context已經表示為AbilityB中賦值的UIAbilityContext內容。

@Entry
@Component
struct Index {
  onPageShow() {
    let ctx = globalThis.context; // Page中從globalThis中取出context並使用
    let permissions = ['com.example.permission']
    ctx.requestPermissionsFromUser(permissions,(result) => {
      console.info('requestPermissionsFromUser result:' + JSON.stringify(result));
    });
  }
  // 頁面展示
  build() {
    // ...
  }
}

5、在AbilityB例項切換至後臺,將AbilityA例項從後臺切換回到前臺。此時AbilityA的onCreate生命週期不會再次進入。

import UIAbility from '@ohos.app.ability.UIAbility'

export default class AbilityA extends UIAbility {
    onCreate(want, launch) { // AbilityA從後臺進入前臺,不會再走這個生命週期
        globalThis.context = this.context;
        // ...
    }
}

6、在AbilityA的頁面再次回到前臺時,其獲取到的globalThis.context表示的為AbilityB的UIAbilityContext,而不是AbilityA的UIAbilityContext,在AbilityA的頁面中使用則會出錯。

@Entry
@Component
struct Index {
  onPageShow() {
    let ctx = globalThis.context; // 這時候globalThis中的context是AbilityB的context
    let permissions=['com.example.permission'];
    ctx.requestPermissionsFromUser(permissions,(result) => { // 使用這個物件就會導致程序崩潰
       console.info('requestPermissionsFromUser result:' + JSON.stringify(result));
    });
  }
  // 頁面展示
  build() {
    // ...
  }
}

🔎6.UIAbility元件間互動(裝置內)

🦋6.1 啟動應用內的UIAbility

1、啟動方

let wantInfo = {
    deviceId: '', // deviceId為空表示本裝置
    bundleName: 'com.example.myapplication',
    abilityName: 'FuncAbility',
    moduleName: 'module1', // moduleName非必選
    parameters: { // 自定義資訊
        info: '來自EntryAbility Index頁面',
    },
}
// context為呼叫方UIAbility的AbilityContext
this.context.startAbility(wantInfo).then(() => {
    // ...
}).catch((err) => {
    // ...
})

2、接受方

import UIAbility from '@ohos.app.ability.UIAbility';
import Window from '@ohos.window';

export default class FuncAbility extends UIAbility {
    onCreate(want, launchParam) {
    // 接收呼叫方UIAbility傳過來的引數
        let funcAbilityWant = want;
        let info = funcAbilityWant?.parameters?.info;
        // ...
    }
}

在FuncAbility業務完成之後,如需要停止當前UIAbility例項,在FuncAbility中透過呼叫terminateSelf()方法實現。

// context為需要停止的UIAbility例項的AbilityContext
this.context.terminateSelf((err) => {
    // ...
});

🦋6.2 啟動應用內的UIAbility並獲取返回結果

1、啟動方

let wantInfo = {
    deviceId: '', // deviceId為空表示本裝置
    bundleName: 'com.example.myapplication',
    abilityName: 'FuncAbility',
    moduleName: 'module1', // moduleName非必選
    parameters: { // 自定義資訊
        info: '來自EntryAbility Index頁面',
    },
}
// context為呼叫方UIAbility的AbilityContext
this.context.startAbilityForResult(wantInfo).then((data) => {
    // ...
}).catch((err) => {
    // ...
})

2、接受方

const RESULT_CODE: number = 1001;
let abilityResult = {
    resultCode: RESULT_CODE,
    want: {
        bundleName: 'com.example.myapplication',
        abilityName: 'FuncAbility',
        moduleName: 'module1',
        parameters: {
            info: '來自FuncAbility Index頁面',
        },
    },
}
// context為被呼叫方UIAbility的AbilityContext
this.context.terminateSelfWithResult(abilityResult, (err) => {
    // ...
});

3、獲取接受方回撥引數

const RESULT_CODE: number = 1001;

// ...

// context為呼叫方UIAbility的AbilityContext
this.context.startAbilityForResult(want).then((data) => {
    if (data?.resultCode === RESULT_CODE) {
        // 解析被呼叫方UIAbility返回的資訊
        let info = data.want?.parameters?.info;
        // ...
    }
}).catch((err) => {
    // ...
})
🦋6.3 啟動其他應用的UIAbility

1、接受方配置資訊

{
  "module": {
    "abilities": [
      {
        // ...
        "skills": [
          {
            "entities": [
              // ...
              "entity.system.default"
            ],
            "actions": [
              // ...
              "ohos.want.action.viewData"
            ]
          }
        ]
      }
    ]
  }
}

2、啟動方

let wantInfo = {
    deviceId: '', // deviceId為空表示本裝置
    // 如果希望隱式僅在特定的捆綁包中進行查詢,請取消下面的註釋。
    // bundleName: 'com.example.myapplication',
    action: 'ohos.want.action.viewData',
    // entities可以被省略。
    entities: ['entity.system.default'],
}

// context為呼叫方UIAbility的AbilityContext
this.context.startAbility(wantInfo).then(() => {
    // ...
}).catch((err) => {
    // ...
})

image

完成後不要忘了,停止

// context為需要停止的UIAbility例項的AbilityContext
this.context.terminateSelf((err) => {
    // ...
});

🦋6.4 啟動其他應用的UIAbility並獲取返回結果

1、接受方配置資訊

{
  "module": {
    "abilities": [
      {
        // ...
        "skills": [
          {
            "entities": [
              // ...
              "entity.system.default"
            ],
            "actions": [
              // ...
              "ohos.want.action.editData"
            ]
          }
        ]
      }
    ]
  }
}

2、啟動方

let wantInfo = {
    deviceId: '', // deviceId為空表示本裝置
    // uncomment line below if wish to implicitly query only in the specific bundle.
    // bundleName: 'com.example.myapplication',
    action: 'ohos.want.action.editData',
    // entities can be omitted.
    entities: ['entity.system.default'],
}

// context為呼叫方UIAbility的AbilityContext
this.context.startAbilityForResult(wantInfo).then((data) => {
    // ...
}).catch((err) => {
    // ...
})
const RESULT_CODE: number = 1001;
let abilityResult = {
    resultCode: RESULT_CODE,
    want: {
        bundleName: 'com.example.myapplication',
        abilityName: 'EntryAbility',
        moduleName: 'entry',
        parameters: {
            payResult: 'OKay',
        },
    },
}
// context為被呼叫方UIAbility的AbilityContext,返回引數
this.context.terminateSelfWithResult(abilityResult, (err) => {
    // ...
});

3、接收引數

const RESULT_CODE: number = 1001;

let want = {
  // Want引數資訊
};

// context為呼叫方UIAbility的AbilityContext
this.context.startAbilityForResult(want).then((data) => {
    if (data?.resultCode === RESULT_CODE) {
        // 解析被呼叫方UIAbility返回的資訊
        let payResult = data.want?.parameters?.payResult;
        // ...
    }
}).catch((err) => {
    // ...
})

🦋6.5 啟動UIAbility的指定頁面

☀️6.5.1 呼叫方UIAbility指定啟動頁面
let wantInfo = {
    deviceId: '', // deviceId為空表示本裝置
    bundleName: 'com.example.myapplication',
    abilityName: 'FuncAbility',
    moduleName: 'module1', // moduleName非必選
    parameters: { // 自定義引數傳遞頁面資訊
        router: 'funcA',
    },
}
// context為呼叫方UIAbility的AbilityContext
this.context.startAbility(wantInfo).then(() => {
    // ...
}).catch((err) => {
    // ...
})
☀️6.5.2 目標UIAbility首次啟動
import UIAbility from '@ohos.app.ability.UIAbility'
import Window from '@ohos.window'

export default class FuncAbility extends UIAbility {
    funcAbilityWant;

    onCreate(want, launchParam) {
        // 接收呼叫方UIAbility傳過來的引數
        this.funcAbilityWant = want;
    }

    onWindowStageCreate(windowStage: Window.WindowStage) {
        // Main window is created, set main page for this ability
        let url = 'pages/Index';
        if (this.funcAbilityWant?.parameters?.router) {
            if (this.funcAbilityWant.parameters.router === 'funA') {
                url = 'pages/Second';
            }
        }
        windowStage.loadContent(url, (err, data) => {
            // ...
        });
    }
}
☀️6.5.3 目標UIAbility非首次啟動

image

1、接收方

UIAbility例項之前已經建立完成,此時會進入UIAbility的onNewWant()回撥中且不會進入onCreate()和onWindowStageCreate()生命週期回撥

import UIAbility from '@ohos.app.ability.UIAbility'

export default class FuncAbility extends UIAbility {
    onNewWant(want, launchParam) {
        // 接收呼叫方UIAbility傳過來的引數
        globalThis.funcAbilityWant = want;
        // ...
    }
}

2、更新頁面UI

FuncAbility對應的Index頁面是處於啟用狀態,不會重新變數宣告以及進入aboutToAppear()生命週期回撥中。因此可以在Index頁面的onPageShow()生命週期回撥中實現頁面路由跳轉的功能。

import router from '@ohos.router';

@Entry
@Component
struct Index {
  onPageShow() {
    let funcAbilityWant = globalThis.funcAbilityWant;
    let url2 = funcAbilityWant?.parameters?.router;
    if (url2 && url2 === 'funcA') {
      router.replaceUrl({
        url: 'pages/Second',
      })
    }
  }

  // 頁面展示
  build() {
    // ...
  }
}

🦋6.6 透過Call呼叫實現UIAbility互動(僅對系統應用開放)

☀️6.6.1 介面說明

Call呼叫是UIAbility能力的一種擴充套件,它允許UIAbility能力被外部呼叫,並進行通訊。Call呼叫支援前臺和後臺兩種啟動方式,使得UIAbility能夠在前臺展示UI或者在後臺建立並執行。透過Call呼叫,呼叫方和被呼叫方之間建立了IPC通訊,應用開發者可以利用Call呼叫實現不同UIAbility之間的資料共享。

更多鴻蒙最新技術知識點,請關注作者部落格:https://t.doruo.cn/14DjR1rEY

Call呼叫的核心介面是startAbilityByCall方法,與startAbility介面相比,startAbilityByCall具有以下不同之處:

  1. startAbilityByCall支援前臺和後臺兩種啟動方式,而startAbility僅支援前臺啟動。

  2. 呼叫方可以使用startAbilityByCall返回的Caller物件與被呼叫方進行通訊,而startAbility沒有通訊能力。

Call呼叫的使用場景主要包括:

  1. 需要與被啟動的UIAbility進行通訊。

  2. 希望被啟動的UIAbility在後臺執行。

image

image

☀️6.6.2 開發步驟(建立Callee被呼叫端)

1、配置Ability的啟動模式

"abilities":[{
  "name": ".CalleeAbility",
  "srcEntrance": "./ets/CalleeAbility/CalleeAbility.ts",
  "launchType": "singleton",
  "description": "$string:CalleeAbility_desc",
  "icon": "$media:icon",
  "label": "$string:CalleeAbility_label",
  "visible": true
}]

2、匯入模組

import Ability from '@ohos.app.ability.UIAbility';

3、定義約束

export default class MyParcelable {
    num: number = 0
    str: string = ""

    constructor(num, string) {
        this.num = num
        this.str = string
    }

    marshalling(messageSequence) {
        messageSequence.writeInt(this.num)
        messageSequence.writeString(this.str)
        return true
    }

    unmarshalling(messageSequence) {
        this.num = messageSequence.readInt()
        this.str = messageSequence.readString()
        return true
    }
}

4、註冊監聽和解除監聽

const TAG: string = '[CalleeAbility]';
const MSG_SEND_METHOD: string = 'CallSendMsg';

function sendMsgCallback(data) {
    console.info('CalleeSortFunc called');

    // 獲取Caller傳送的序列化資料
    let receivedData = new MyParcelable(0, '');
    data.readParcelable(receivedData);
    console.info(`receiveData[${receivedData.num}, ${receivedData.str}]`);

    // 作相應處理
    // 返回序列化資料result給Caller
    return new MyParcelable(receivedData.num + 1, `send ${receivedData.str} succeed`);
}

export default class CalleeAbility extends Ability {
    onCreate(want, launchParam) {
        try {
            this.callee.on(MSG_SEND_METHOD, sendMsgCallback);
        } catch (error) {
            console.info(`${MSG_SEND_METHOD} register failed with error ${JSON.stringify(error)}`);
        }
    }

    onDestroy() {
        try {
            this.callee.off(MSG_SEND_METHOD);
        } catch (error) {
            console.error(TAG, `${MSG_SEND_METHOD} unregister failed with error ${JSON.stringify(error)}`);
        }
    }
}
☀️6.6.3 開發步驟(訪問Callee被呼叫端)

1、匯入模組

import Ability from '@ohos.app.ability.UIAbility';

2、獲取Caller通訊介面

// 註冊caller的release監聽
private regOnRelease(caller) {
    try {
        caller.on("release", (msg) => {
            console.info(`caller onRelease is called ${msg}`);
        })
        console.info('caller register OnRelease succeed');
    } catch (error) {
        console.info(`caller register OnRelease failed with ${error}`);
    }
}

async onButtonGetCaller() {
    try {
        this.caller = await context.startAbilityByCall({
            bundleName: 'com.samples.CallApplication',
            abilityName: 'CalleeAbility'
        })
        if (this.caller === undefined) {
            console.info('get caller failed')
            return
        }
        console.info('get caller success')
        this.regOnRelease(this.caller)
    } catch (error) {
        console.info(`get caller failed with ${error}`)
    }
}

🚀寫在最後

  • 如果你覺得這篇內容對你還蠻有幫助,我想邀請你幫我三個小忙:
  • 點贊,轉發,有你們的 『點贊和評論』,才是我創造的動力。
  • 關注小編,同時可以期待後續文章ing🚀,不定期分享原創知識。
  • 更多鴻蒙最新技術知識點,請關注作者部落格:https://t.doruo.cn/14DjR1rEY

image

相關文章