鸿蒙开发:深入浅出Stage模型(UIAbility组件)

news2024/12/23 17:59:50

🚀一、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组件启动页面背景颜色资源文件的索引
        // ...
      }
    ]
  }
}

在这里插入图片描述

🔎2.生命周期

在HarmonyOS中,Stage模型指的是应用程序的页面层级结构管理方式,通过Stage模型可以实现页面的切换和管理。UIAbility组件是在Stage模型中管理的页面组件,它具备自己的生命周期。

UIAbility的生命周期包括Create、Foreground、Background、Destroy四个状态,如下图所示。

在这里插入图片描述

🦋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) {
        // 应用初始化
    }
    // ...
}

在这里插入图片描述

☀️2.1.2 WindowStageCreate和WindowStageDestroy状态

WindowStageCreate和WindowStageDestroy是用于创建和销毁窗口阶段的方法。

WindowStageCreate方法用于创建一个新的窗口阶段,并返回该阶段的句柄。通过该句柄,可以对窗口阶段进行操作,如设置窗口的大小、位置、背景等。

WindowStageDestroy方法用于销毁一个窗口阶段,并释放相关的资源。在调用该方法之前,需要先解除与窗口阶段相关的所有绑定,以确保资源能够被正确释放。

在这里插入图片描述

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界面资源
    }
}

在这里插入图片描述

☀️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界面不可见时无用的资源,或者在此回调中执行较为耗时的操作
        // 例如状态保存等
    }
}

在这里插入图片描述

☀️2.1.4 Destroy状态

Destroy状态是指应用程序已经被完全销毁的状态。当应用程序处于Destroy状态时,它的所有资源都被释放,没有任何活动在进行。这意味着应用程序不再存在于系统的活动栈中,并且无法通过返回键或其他方式重新进入。

当应用程序处于Destroy状态时,系统可能会回收应用程序的内存和其他资源,以便为其他应用程序或系统提供更多资源。在Destroy状态下,应用程序的生命周期已经结束,并且它不再对用户可见。

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() {
        // 系统资源的释放、数据的保存等
    }
}

在这里插入图片描述

🔎3.启动模式

🦋3.1 singleton(单实例模式)

单例模式是一种创建只允许存在一个实例的设计模式。在单例模式中,一个类只能创建一个对象,并且提供一个访问该对象的全局访问点。

使用单例模式的主要目的是确保在整个应用程序中只有一个实例,这样可以节省系统资源,并且可以方便地控制对该实例的访问。

单例模式通常采用以下方式实现:

  1. 私有化构造函数:将类的构造函数设置为私有,使得其他类无法直接创建该类的实例。
  2. 提供一个静态方法或属性来获取实例:由于无法直接创建实例,需要提供一个静态方法或属性来获取类的唯一实例。该方法或属性会在第一次调用时创建实例,并在以后的调用中返回同一个实例。
  3. 线程安全:由于多线程环境下,可能会出现多个线程同时调用获取实例的方法,并尝试创建实例的情况。为了确保线程安全,可以使用同步锁或双重检查锁定机制来保证只有一个线程能够创建实例。

使用单例模式时需要注意以下几点:

  1. 单例模式会引入全局状态,可能会增加系统的复杂性和耦合度。
  2. 单例模式不适合有多个实例场景的情况。
  3. 单例模式可能会导致单例实例的内存泄漏问题,因为该实例在整个应用程序的生命周期中保持存在。
  4. 单例模式可能会影响代码的可测试性,因为单例实例会在测试中被共享,可能会导致测试结果受到其他测试用例的影响。

在HarmonyOS中每次调用startAbility()方法时,如果应用进程中该类型的UIAbility实例已经存在,则复用系统中的UIAbility实例。系统中只存在唯一一个该UIAbility实例,即在最近任务列表中只存在一个该类型的UIAbility实例。

单例模式配置如下:

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

在这里插入图片描述

🦋3.2 multiton(多实例模式)

Multiton(多实例模式)是设计模式中的一种,它是单例模式的一种变体。在单例模式中,一个类只能有一个实例;而在多实例模式中,一个类可以拥有多个实例,每个实例有不同的标识符或键值。每个实例都可以被独立访问,类似于一个字典结构,可以根据不同的键值获取对应的实例。

Multiton模式通常用于需要创建一组有限且确定数量的类实例的场景。每个实例都有自己的独立状态和行为,但是可以通过共享某些公共资源进行协作。Multiton模式可以提供更大的灵活性和可扩展性,同时保持每个实例的独立性。

Multiton模式可以通过使用一个字典或者映射结构来维护每个实例的集合,并通过一个键值来唯一标识每个实例。当需要获取一个实例时,可以根据提供的键值从集合中获取对应的实例。如果集合中不存在对应的实例,则可以创建一个新的实例并添加到集合中。

Multiton模式的优点包括可以灵活地管理和访问多个实例,提供更细粒度的控制,同时每个实例都是独立的,可以有自己独立的状态和行为。但是Multiton模式也存在一些缺点,例如需要额外的管理和维护工作,同时可能会导致增加内存消耗。因此,在使用Multiton模式时需要根据具体的需求和场景来进行权衡和选择。

多实例模式配置如下:

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

在这里插入图片描述

🦋3.3 specified(指定实例模式)

指定实例模式(specified instance pattern)用于限定一个类只能有固定数目的实例。在这种模式中,类的实例被限制为预先定义的数量,且这些实例在运行时不能被创建或销毁。

指定实例模式常用于需要限制某个类的实例数量的场景。例如,一个数据库连接池的实现可以使用指定实例模式来限制连接的数量,确保不会创建过多的连接。另一个例子是线程池,可以使用指定实例模式来限制线程的数量。

在指定实例模式中,通常会维护一个内部的实例列表,来跟踪已经创建的实例。当需要获取一个实例时,会首先检查这个列表,如果列表中已经有足够的实例,则直接返回一个实例给调用者。如果列表中没有足够的实例,则根据需求创建新的实例,并添加到列表中。当实例不再需要时,通常会将其从列表中移除,以便可以继续创建新的实例。

指定实例模式可以有效地控制类的实例数量,避免了资源的浪费和过度创建实例的问题。然而,由于实例数量是固定的,所以可能会出现竞争条件和资源瓶颈的问题,需要进行合理的设计和调优。

指定实例模式配置如下:

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

在这里插入图片描述

案例说明:

例如有两个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) => {
            // ...
        });
    }

    // ...
}

在这里插入图片描述

🦋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;

        // ...
    }
}

在这里插入图片描述
在页面获取

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的数据同步

主要有两种方式:

方式描述
EventHub基于发布订阅模式来实现,事件需要先订阅后发布,订阅者收到消息后进行处理。
globalThisArkTS引擎实例内部的一个全局对象,在ArkTS引擎实例内部都能访问。

🦋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));
        });
    }
}

在这里插入图片描述

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()来输出日志。

虽然globalThis提供了一种通用的访问全局对象的方式,但在实际编程中还是建议根据具体的环境来使用相应的全局对象。
在这里插入图片描述

☀️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使用的注意事项

在这里插入图片描述

案例:

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) => {
    // ...
})

在这里插入图片描述

完成后不要忘了,停止

// 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非首次启动

在这里插入图片描述
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之间的数据共享。

Call调用的核心接口是startAbilityByCall方法,与startAbility接口相比,startAbilityByCall具有以下不同之处:

  1. startAbilityByCall支持前台和后台两种启动方式,而startAbility仅支持前台启动。

  2. 调用方可以使用startAbilityByCall返回的Caller对象与被调用方进行通信,而startAbility没有通信能力。

Call调用的使用场景主要包括:

  1. 需要与被启动的UIAbility进行通信。

  2. 希望被启动的UIAbility在后台运行。

名词描述
CallerAbility进行Call调用的UIAbility(调用方)。
CalleeAbility被Call调用的UIAbility(被调用方)。
Caller实际对象,由startAbilityByCall接口返回,CallerAbility可使用Caller与CalleeAbility进行通信。
Callee实际对象,被CalleeAbility持有,可与Caller进行通信。

在这里插入图片描述

☀️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}`)
    }
}

看完三件事❤️

  • 如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙:
  • 点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。
  • 关注作者龚众皓: 『 蜀道衫 』,不定期分享原创知识。
  • 关注后回复【666】扫码即可获取学习文档。
  • 同时可以期待后续文章ing🚀。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2082592.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Qt笔记】QCommandLinkButton控件详解

目录 引言 一、概述 二、特性与属性 1. 属性 2. 样式 三、基本用法 1. 引入必要的头文件 2. 创建和配置 QCommandLinkButton 3. 布局管理 四、高级用法 1. 自定义绘制 2. 动态内容更新 五、代码解析示例 注意 总结 引言 QCommandLinkButton 是 Qt 框架中 QtWi…

招生简章如何制作为在线版的网址链接?

​随着互联网的高速发展,越来越多的学校和企业选择通过在线方式发布招生简章。这种趋势不仅方便了大量用户,还大大降低了传统纸质招生简章的制造成本。那么,如何制作一份吸引眼球的在线招生简章呢? 1.要制作电子杂志,首先需要选择…

leetcode_001_两数之和解析

两数之和解析 题目: 给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。你可以假设每种输入只会对应一个答案,并且你不能使用两次相同的元素。你可以按任意顺序…

【解压即玩】使命de召唤4

《使命de召唤4》 立即下载:【chumenx.com】【解压即玩】使命de召唤4

Leetcode 51. 皇后 回溯 C++实现

Leetcode 51. 皇后 问题:按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。 n 皇后问题 研究的是如何将 n 个皇后放置在 nn 的棋盘上,并且使皇后彼此之间不能相互攻击。 给你一个整数 n ,返回所有不…

Spring框架基础

前言 本文将讲解spring框架的基础内容 Spring 首先,我们需要了解,spring是什么? spring是一个轻量级的IOC和AOP的一站式java开发框架 作用是简化企业级开发 轻量级:框架体积小(核心模块) 其中 IOC: Inversion of Control 控制反转 IOC的作用是可以把创建对象的控制权,反转给s…

学习区块链?看我就够了!

写在前面,本文提到的所有书籍资源,都提供免费下载地址 内含pdf,epub,mobi等格式 一.区块链:定义未来金融与经济新格局 以区块链技术为核心构建的价值互联网 将深刻改变未来的金融与经济格局 文明向前发展,有些成果的…

Linux基础软件-共享存储nfs

作者介绍:简历上没有一个精通的运维工程师。希望大家多多关注作者,下面的思维导图也是预计更新的内容和当前进度(不定时更新)。 Linux进阶部分又分了很多小的部分,我们刚讲完了Linux日常运维。讲的那些东西都算是系统自带的,但是Linux作为一个…

大模型之二十七-语音识别Whisper实例浅析

Whisper简介 Whisper是OpenAI于2022年9月开源的一个多语种识别模型,目前支持99种语言,是目前性能最好的开源多语种识别ASR大模型,第一版版使用了68万小时标注好的语料预训练模型,而large-v3的标注数据超过了500万小时&#xff0c…

QL5010-16-ASEMI逆变焊机专用整流桥QL5010

编辑:ll QL5010-16-ASEMI逆变焊机专用整流桥QL5010 型号:QL5010 品牌:ASEMI 封装:KBPC-4 批号:2024 类型:整流模块 电流:50A 电压:1600V 安装方式:直插式封装 …

(2)Studio 5000 Logix Emulate仿真使用方法

Studio 5000 Logix Emulate仿真使用方法 引言:首先要安装好Studio 5000 logix emulate,这个软件也是需要授权的。还需要注意仿真器与studio 5000之间版本的对应,studio5000的版本需要小于仿真器版本。 打开Studio 5000 logix emulate&#x…

Lombok组件的使用

什么是Lombok Lombok是一个Java库,能自动插入编辑器并构建工具,简化Java开发。通过添加注解的方式,不需要为类编写getter或eques方法,同时可以自动化日志变量。 Lombok的使用 使用Lombok需要的开发环境JavaMavenIntelliJ IDEA或…

游戏开发设计模式之桥接模式

目录 桥接模式在游戏开发中的具体应用案例是什么? 如何在Unity或Unreal Engine中实现桥接模式以提高游戏引擎与场景的灵活性? 桥接模式与其他设计模式(如适配器模式、模板方法模式)在游戏开发中的比较优势是什么? …

【Verilog 数字系统设计教程】Verilog 基础:硬件描述语言入门指南

目录 摘要 1. 引言 2. Verilog 历史与发展 3. Verilog 基本语法 4. Verilog 模块与端口 5. 组合逻辑与时序逻辑 6. 时钟域与同步设计 7. 测试与仿真 8. Verilog 高级特性 任务(Tasks) 函数(Functions) 多维数组 结构体…

MinGW-w64 x86_64-8.1.0-release-posix-seh-rt_v6-rev0.7z

MinGW-w64 x86_64-8.1.0-release-posix-seh-rt_v6-rev0.7z 安装包 链接: https://pan.baidu.com/s/1G8SDlDcBtNU4TCBuLL9XYw?pwdv587 提取码: v587 是7z文件,解压即可,自动得到一个mingw64文件夹。 自存,应该能用

多态(虚构的整体,具体的个体)(多态的基本概念/多态的原理剖析/纯虚函数和抽象类/虚析构和纯虚析构)

多态的基本概念 #define _CRT_SECURE_NO_WARNINGS #include<iostream> using namespace std; // 多态的基本概念 // 多态分为静态多态和动态多态 // 静态多态&#xff1a; 函数重载还运算符重载属于静态多态&#xff0c;服用函数名 // 动态多态&#xff1a; 派生派和虚函…

电商走向“未来化”,“含金量”几何?

2018年&#xff0c;史蒂文斯皮尔伯格导演的《头号玩家》一经上映&#xff0c;就带火了虚拟现实概念。电影中&#xff0c;男主角戴上VR眼镜、感知手套&#xff0c;穿上触感套装&#xff0c;从视觉、听觉到触觉&#xff0c;瞬间切换至全新的世界。 电影《头号玩家》剧照 时隔六年…

如何使用ssm实现图书管理借阅系统

TOC ssm301图书管理借阅系统jsp 绪论 1.1 研究背景 当前社会各行业领域竞争压力非常大&#xff0c;随着当前时代的信息化&#xff0c;科学化发展&#xff0c;让社会各行业领域都争相使用新的信息技术&#xff0c;对行业内的各种相关数据进行科学化&#xff0c;规范化管理。…

构建在线教育系统源码:企业培训APP开发的技术指南

在数字化浪潮的推动下&#xff0c;企业培训正从传统课堂转向在线教育模式。构建一个高效、稳定且可扩展的在线教育系统源码&#xff0c;已经成为开发企业培训APP的关键。在本文中&#xff0c;我们将深入探讨构建在线教育系统源码的核心技术&#xff0c;并提供一份开发企业培训A…

免费分享一套SpringBoot+Vue个人理财管理系统【论文+源码+SQL脚本】,帅呆了~~

大家好&#xff0c;我是java1234_小锋老师&#xff0c;看到一个不错的SpringBootVue个人理财管理系统&#xff0c;分享下哈。 项目视频演示 【免费】SpringbootVue个人理财管理系统 Java毕业设计_哔哩哔哩_bilibili 项目介绍 随着信息技术在管理上越来越深入而广泛的应用&am…