From 3ccad06ae6dd289763878124a517ad2498711494 Mon Sep 17 00:00:00 2001 From: wusongqing Date: Mon, 28 Mar 2022 22:03:10 +0800 Subject: [PATCH] Added stage-ability and stage-call Signed-off-by: wusongqing --- en/application-dev/ability/Readme-EN.md | 10 + .../ability/figures/stage-call.png | Bin 0 -> 3003 bytes en/application-dev/ability/stage-ability.md | 267 ++++++++++++++++++ en/application-dev/ability/stage-call.md | 250 ++++++++++++++++ 4 files changed, 527 insertions(+) create mode 100644 en/application-dev/ability/figures/stage-call.png create mode 100644 en/application-dev/ability/stage-ability.md create mode 100644 en/application-dev/ability/stage-call.md diff --git a/en/application-dev/ability/Readme-EN.md b/en/application-dev/ability/Readme-EN.md index e0fb613133..9c9a162897 100644 --- a/en/application-dev/ability/Readme-EN.md +++ b/en/application-dev/ability/Readme-EN.md @@ -6,5 +6,15 @@ - [Service Ability Development](fa-serviceability.md) - [Data Ability Development](fa-dataability.md) - [FA Widget Development](fa-formability.md) + - Stage Model + - [Stage Model Overview](stage-brief.md) + - [Ability Development](stage-ability.md) + - [Service Extension Ability Development](stage-serviceextension.md) + - [Ability Continuation Development](stage-ability-continuation.md) + - [Ability Call Development](stage-call.md) + - [Stage Widget Development](stage-formextension.md) - Other + - [WantAgent Development](wantagent.md) - [Ability Assistant Usage](ability-assistant-guidelines.md) + - [Test Framework Usage](ability-delegator.md) + diff --git a/en/application-dev/ability/figures/stage-call.png b/en/application-dev/ability/figures/stage-call.png new file mode 100644 index 0000000000000000000000000000000000000000..28f2a0f7ea9d86fc81e0c1a37d556384b14a9bdd GIT binary patch literal 3003 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 ZpSStGYr~$&^v@7~#vI2TE4THd{u?zmTL=IE literal 0 HcmV?d00001 diff --git a/en/application-dev/ability/stage-ability.md b/en/application-dev/ability/stage-ability.md new file mode 100644 index 0000000000..c33df53cad --- /dev/null +++ b/en/application-dev/ability/stage-ability.md @@ -0,0 +1,267 @@ +# Ability Development +## When to Use +The stage model is an application development model introduced in API version 9. For details about this model, see [Stage Model Overview](stage-brief.md). To develop an ability based on the stage model, you must implement the following logic: +- Create Page abilities for an application that needs to be browsed on the screen and supports man-machine interaction, such as video playback and news browsing. +- Obtain ability configuration information, such as **ApplicationInfo**, **AbilityInfo**, and **HapModuleInfo**. +- Start an ability, start an ability with start options, start an ability with the returned result, or start an ability with an account ID. +- Request certain permissions from end users. +- Notify the ability stage and ability of environment configuration changes. +- Call common components. For details, see [Call Development](stage-call.md). +- Connect to and disconnect from the Service ability. For details, see [Service Extension Ability Development](stage-serviceextension.md). +- Continue the application on another device. For details, see [Ability Continuation Development](stage-ability-continuation.md). + +## Available APIs +The table below describes the APIs provided by the **AbilityStage** class, which has the **context** attribute. For details about the APIs, see [AbilityStage](../reference/apis/js-apis-application-abilitystage.md). + +**Table 1** AbilityStage APIs +|API|Description| +|:------|:------| +|void onCreate()|Called when an ability stage is created.| +|string onAcceptWant(want: Want)|Called when a specified ability is started.| +|void onConfigurationUpdated(config: Configuration)|Called when the global configuration is updated.| + +The table below describes the APIs provided by the **Ability** class. For details about the APIs, see [Ability](../reference/apis/js-apis-application-ability.md). + +**Table 2** Ability APIs +|API|Description| +|:------|:------| +|void onCreate(want: Want, param: AbilityConstant.LaunchParam)|Called when an ability is created.| +|void onDestroy()|Called when the ability is destroyed.| +|void onWindowStageCreate(windowStage: window.WindowStage)|Called when a **WindowStage** is created for the ability. You can use the **window.WindowStage** APIs to implement operations such as page loading.| +|void onWindowStageDestroy()|Called when the **WindowStage** is destroyed for the ability.| +|void onForeground()|Called when the ability is running in the foreground.| +|void onBackground()|Called when the ability is switched to the background.| +|void onNewWant(want: Want)|Called when the ability startup mode is set to singleton.| +|void onConfigurationUpdated(config: Configuration)|Called when the configuration of the environment where the ability is running is updated.| + +The **Ability** class has the **context** attribute, which belongs to the **AbilityContext** class. The **AbilityContext** class has attributes such as **abilityInfo** and **currentHapModuleInfo**. For details about the APIs, see [AbilityContext](../reference/apis/js-apis-ability-context.md). + +**Table 3** AbilityContext APIs +|API|Description| +|:------|:------| +|void startAbility(want: Want, callback: AsyncCallback)|Starts an ability.| +|void startAbility(want: Want, options: StartOptions, callback: AsyncCallback)|Starts an ability with start options.| +|void startAbilityWithAccount(want: Want, accountId: number, callback: AsyncCallback)|Starts an ability with the account ID.| +|void startAbilityWithAccount(want: Want, accountId: number, options: StartOptions, callback: AsyncCallback)|Starts an ability with the account ID and start options.| +|void startAbilityForResult(want: Want, callback: AsyncCallback)|Starts an ability with the returned result.| +|void startAbilityForResult(want: Want, options: StartOptions, callback: AsyncCallback)|Starts an ability with the returned result and start options.| +|void startAbilityForResultWithAccount(want: Want, accountId: number, callback: AsyncCallback)|Starts an ability with the returned result and account ID.| +|void startAbilityForResultWithAccount(want: Want, accountId: number, options: StartOptions, callback: AsyncCallback)|Starts an ability with the returned result, account ID, and start options.| +|void terminateSelf(callback: AsyncCallback)|Destroys the Page ability.| +|void terminateSelfWithResult(parameter: AbilityResult, callback: AsyncCallback)|Destroys the Page ability with the returned result.| + +## How to Develop +### Creating Page Abilities for an Application +To create Page abilities for an application on the stage model, you must implement the **AbilityStage** class and ability lifecycle callbacks, and use the **Window** APIs to set the pages. The sample code is as follows: +1. Import the **AbilityStage** module. + ``` + import AbilityStage from "@ohos.application.AbilityStage" + ``` +2. Implement the **AbilityStage** class. + ```ts + export default class MyAbilityStage extends AbilityStage { + onCreate() { + console.log("MyAbilityStage onCreate") + } + } + ``` +3. Import the **Ability** module. + ```js + import Ability from '@ohos.application.Ability' + ``` +4. Implement the lifecycle callbacks of the **Ability** class. + + In the **onWindowStageCreate(windowStage)** API, use **loadContent** to set the pages to be loaded by the application. For details about how to use the **Window** APIs, see [Window Development](../windowmanager/window-guidelines.md). + ```ts + export default class MainAbility extends Ability { + onCreate(want, launchParam) { + console.log("MainAbility onCreate") + } + + onDestroy() { + console.log("MainAbility onDestroy") + } + + onWindowStageCreate(windowStage) { + console.log("MainAbility onWindowStageCreate") + + windowStage.loadContent("pages/index").then((data) => { + console.log("MainAbility load content succeed with data: " + JSON.stringify(data)) + }).catch((error) => { + console.error("MainAbility load content failed with error: "+ JSON.stringify(error)) + }) + } + + onWindowStageDestroy() { + console.log("MainAbility onWindowStageDestroy") + } + + onForeground() { + console.log("MainAbility onForeground") + } + + onBackground() { + console.log("MainAbility onBackground") + } + } + ``` +### Obtaining AbilityStage and Ability Configuration Information +Both the **AbilityStage** and **Ability** classes have the **context** attribute. An application can obtain the context of the **Ability** instance through **this.context** to obtain detailed configuration information. The following example shows how the ability stage obtains the bundle code directory, HAP file name, ability name, and system language through the **context** attribute. The sample code is as follows: +```ts +import AbilityStage from "@ohos.application.AbilityStage" +export default class MyAbilityStage extends AbilityStage { + onCreate() { + console.log("MyAbilityStage onCreate") + let context = this.context + console.log("MyAbilityStage bundleCodeDir" + context.bundleCodeDir) + + let currentHapModuleInfo = context.currentHapModuleInfo + console.log("MyAbilityStage hap module name" + currentHapModuleInfo.name) + console.log("MyAbilityStage hap module mainAbilityName" + currentHapModuleInfo.mainAbilityName) + + let config = this.context.config + console.log("MyAbilityStage config language" + config.language) + } +} +``` + +The following example shows how the ability obtains the bundle code directory, HAP file name, ability name, and system language through the **context** attribute. The sample code is as follows: +```ts +import Ability from '@ohos.application.Ability' +export default class MainAbility extends Ability { + onCreate(want, launchParam) { + console.log("MainAbility onCreate") + let context = this.context + console.log("MainAbility bundleCodeDir" + context.bundleCodeDir) + + let abilityInfo = this.context.abilityInfo; + console.log("MainAbility ability bundleName" + abilityInfo.bundleName) + console.log("MainAbility ability name" + abilityInfo.name) + + let config = this.context.config + console.log("MyAbilityStage config language" + config.language) + } +} +``` + +### Starting an Ability +An application can obtain the context of an **Ability** instance through **this.context** and then use the **StartAbility** API in the **AbilityContext** class to start the ability. The ability can be started by specifying **Want**, **StartOptions**, and **accountId**, and the operation result can be returned using a callback or **Promise** instance. The sample code is as follows: +```ts +let context = this.context +var want = { + "deviceId": "", + "bundleName": "com.example.MyApplication", + "abilityName": "MainAbility" +}; +var options = { + windowMode: 0, + displayId: 2 +}; +context.startAbility(want, options).then((data) => { + console.log("Succeed to start ability with data: " + JSON.stringify(data)) +}).catch((error) => { + console.error("Failed to start ability with error: "+ JSON.stringify(error)) +}) +``` + +### Starting an Ability on a Remote Device (Available only to System Applications) +>Note: The **getTrustedDeviceListSync** API of the **DeviceManager** class is open only to system applications. Therefore, cross-device ability startup applies only to system applications. +In the cross-device scenario, you must specify the ID of the remote device. The sample code is as follows: +```ts +let context = this.context +var want = { + "deviceId": getRemoteDeviceId(), + "bundleName": "com.example.MyApplication", + "abilityName": "MainAbility" +}; +context.startAbility(want).then((data) => { + console.log("Succeed to start remote ability with data: " + JSON.stringify(data)) +}).catch((error) => { + console.error("Failed to start remote ability with error: "+ JSON.stringify(error)) +}) +``` +Obtain the ID of a specified device from **DeviceManager**. The sample code is as follows: +```ts +import deviceManager from '@ohos.distributedHardware.deviceManager'; +function getRemoteDeviceId() { + if (typeof dmClass === 'object' && dmClass != null) { + var list = dmClass.getTrustedDeviceListSync(); + if (typeof (list) == 'undefined' || typeof (list.length) == 'undefined') { + console.log("MainAbility onButtonClick getRemoteDeviceId err: list is null"); + return; + } + console.log("MainAbility onButtonClick getRemoteDeviceId success:" + list[0].deviceId); + return list[0].deviceId; + } else { + console.log("MainAbility onButtonClick getRemoteDeviceId err: dmClass is null"); + } +} +``` + +### Requesting Permissions +If an application requires certain permissions, such as storage, location information, and log access, the application must request the permissions from end users. After determining the required permissions, add the permissions in the **module.json** file and use **requestPermissionsFromUser** to request the permissions from end users in the form of a dialog box. The following uses the permissions for calendar access as an example. +Modify the **module.json** file as follows: +```json +"requestPermissions": [ + { + "name": "ohos.permission.READ_CALENDAR" + } +] +``` +Request the permissions from end users in the form of a dialog box: +```ts +let context = this.context +let permissions: Array = ['ohos.permission.READ_CALENDAR'] +context.requestPermissionsFromUser(permissions).then((data) => { + console.log("Succeed to request permission from user with data: "+ JSON.stringify(data)) +}).catch((error) => { + console.log("Failed to request permission from user with error: "+ JSON.stringify(error)) +}) +``` +In the cross-device scenario, the application must also apply for the data synchronization permission from end users. The sample code is as follows: +```ts +let context = this.context +let permissions: Array = ['ohos.permission.DISTRIBUTED_DATASYNC'] +context.requestPermissionsFromUser(permissions).then((data) => { + console.log("Succeed to request permission from user with data: "+ JSON.stringify(data)) +}).catch((error) => { + console.log("Failed to request permission from user with error: "+ JSON.stringify(error)) +}) +``` + +### Notifying of Environment Configuration Changes +When the global configuration, for example, system language and color mode, changes, the **onConfigurationUpdated** API is called to notify the ability stage and ability. System applications can update the system language and color mode through the **updateConfiguration** API. The following example shows the implement of the **onConfigurationUpdated** callback in the **AbilityStage** class. The callback is triggered when the system language and color mode change. The sample code is as follows: +```ts +import Ability from '@ohos.application.Ability' +import ConfigurationConstant from '@ohos.application.ConfigurationConstant' +export default class MyAbilityStage extends AbilityStage { + onConfigurationUpdated(config) { + console.log("MyAbilityStage onConfigurationUpdated") + console.log("MyAbilityStage config language" + config.language) + console.log("MyAbilityStage config colorMode" + config.colorMode) + } +} +``` + +The following example shows the implement of the **onConfigurationUpdated** callback in the **Ability** class. The callback is triggered when the system language, color mode, and display parameters (such as the direction and density) change. The sample code is as follows: +```ts +import Ability from '@ohos.application.Ability' +import ConfigurationConstant from '@ohos.application.ConfigurationConstant' +export default class MainAbility extends Ability { { + onConfigurationUpdated(config) { + console.log("MainAbility onConfigurationUpdated") + console.log("MainAbility config language" + config.language) + console.log("MainAbility config colorMode" + config.colorMode) + console.log("MainAbility config direction" + config.direction) + console.log("MainAbility config screenDensity" + config.screenDensity) + console.log("MainAbility config displayId" + config.displayId) + } +} +``` + +## Development Example +The following sample is provided to help you better understand how to develop an ability on the stage model: + +[eTSStageCallAbility](https://gitee.com/openharmony/app_samples/tree/master/ability/eTSStageCallAbility) + +In this sample, the **AbilityStage** APIs are implemented in the **AbilityStage.ts** file in the **Application** directory, the **Ability** APIs are implemented in the **MainAbility** directory, and **pages/index** is the pages of the ability. Another ability is implemented in the **CalleeAbility** directory, and its pages are the content configured in **pages/second**. The callee ability can be started from the main ability. diff --git a/en/application-dev/ability/stage-call.md b/en/application-dev/ability/stage-call.md new file mode 100644 index 0000000000..7514bc3074 --- /dev/null +++ b/en/application-dev/ability/stage-call.md @@ -0,0 +1,250 @@ +# Ability Call Development +## When to Use +Ability call is an extension of the ability capabilities. It enables an ability to be invoked by external systems. In this way, the ability can be displayed as a UI page on the foreground and created and run on the background. You can use the **Call** APIs to implement data sharing between different abilities through inter-process communication (IPC). There are two roles in the ability call: caller and callee. The following scenarios are involved in the ability call development: +- Creating a callee +- Accessing the callee + +The following figure shows the ability call process. + +![stage-call](figures/stage-call.png) + +## Available APIs +The table below describes the ability call APIs. For details, see [Ability](../reference/apis/js-apis-application-ability.md#caller). + +**Table 1** Ability call APIs +|API|Description| +|:------|:------| +|Promise startAbilityByCall(want: Want)|Obtains the caller interface of the specified ability, and if the specified ability is not started, starts the ability in the background.| +|void on(method: string, callback: CalleeCallBack)|Callee.on: callback invoked when the callee registers a method.| +|void off(method: string)|Callee.off: callback invoked when the callee deregisters a method.| +|Promise call(method: string, data: rpc.Sequenceable)|Caller.call: sends agreed sequenceable data to the callee.| +|Promise callWithResult(method: string, data: rpc.Sequenceable)|Caller.callWithResult: sends agreed sequenceable data to the callee and returns the agreed sequenceable data.| +|void release()|Caller.release: releases the caller interface.| +|void onRelease(callback: OnReleaseCallBack)|Caller.onRelease: registers a callback that is invoked when the caller is disconnected.| + +## How to Develop +### Creating a Callee +For the callee, implement the callback to receive data and the methods to marshal and unmarshal data. When data needs to be received, use the **on** API to register a listener. When data does not need to be received, use the **off** API to deregister the listener. +1. Configure the ability startup mode. + + Set the ability of the callee to **singleton** in the **module.json5** file. + +|JSON Field|Description| +|:------|:------| +|"launchType"|Ability startup mode. Set this parameter to **singleton**.| + +An example of the ability configuration is as follows: +```json +"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 the **Ability** module. +``` +import Ability from '@ohos.application.Ability' +``` +3. Define the agreed sequenceable data. + + The data formats sent and received by the caller and callee must be consistent. In the following example, the data consists of numbers and strings. The sample code is as follows: +```ts +export default class MySequenceable { + num: number = 0 + str: String = "" + + constructor(num, string) { + this.num = num + this.str = string + } + + marshalling(messageParcel) { + messageParcel.writeInt(this.num) + messageParcel.writeString(this.str) + return true + } + + unmarshalling(messageParcel) { + this.num = messageParcel.readInt() + this.str = messageParcel.readString() + return true + } +} +``` +4. Implement **Callee.on** and **Callee.off**. + + The time to register a listener for the callee depends on your application. The data sent and received before the listener is registered and that after the listener is deregistered are not processed. In the following example, the **CalleeSortMethod** listener is registered in **onCreate** of the ability and deregistered in **onDestroy**. After receiving sequenceable data, the application processes the data and returns them. You need to implement processing based on service requirements. The sample code is as follows: +```ts +const TAG: string = '[CalleeAbility]' +const MSG_SEND_METHOD: string = 'CallSendMsg' + +function sendMsgCallback(data) { + Logger.log(TAG, 'CalleeSortFunc called') + + // Obtain the sequenceable data sent by the caller. + let receivedData = new MySequenceable(0, '') + data.readSequenceable(receivedData) + Logger.log(TAG, `receiveData[${receivedData.num}, ${receivedData.str}]`) + + // Process the data. + // Return the sequenceable data result to the caller. + return new MySequenceable(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) { + Logger.error(TAG, `${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)}`) + } + } +} +``` + +### Accessing the Callee +1. Import the **Ability** module. +``` +import Ability from '@ohos.application.Ability' +``` +2. Obtain the caller interface. + + The **context** attribute of the ability implements **startAbilityByCall** to obtain the caller interface of the ability. The following example uses **this.context** to obtain the **context** attribute of the **Ability** instance, uses **startAbilityByCall** to start the callee, obtain the caller interface, and register the **onRelease** listener of the caller. You need to implement processing based on service requirements. The sample code is as follows: +```ts +async onButtonGetCaller() { + try { + this.caller = await context.startAbilityByCall({ + bundleName: 'com.samples.CallApplication', + abilityName: 'CalleeAbility' + }) + if (this.caller === undefined) { + Logger.error(TAG, 'get caller failed') + return + } + Logger.log(TAG, 'get caller success') + this.regOnRelease(this.caller) + } catch (error) { + Logger.error(TAG, `get caller failed with ${error}`) + } +}.catch((error) => { + console.error(TAG + 'get caller failed with ' + error) +}) +``` +In the cross-device scenario, you need to specify the ID of the peer device. The sample code is as follows: +```ts +let TAG = '[MainAbility] ' +var caller = undefined +let context = this.context + +context.startAbilityByCall({ + deviceId: getRemoteDeviceId(), + bundleName: 'com.samples.CallApplication', + abilityName: 'CalleeAbility' +}).then((data) => { + if (data != null) { + caller = data + console.log(TAG + 'get remote caller success') + // Register the onRelease listener of the caller. + caller.onRelease((msg) => { + console.log(TAG + 'remote caller onRelease is called ' + msg) + }) + console.log(TAG + 'remote caller register OnRelease succeed') + } +}).catch((error) => { + console.error(TAG + 'get remote caller failed with ' + error) +}) +``` +Obtain the ID of the peer device from **DeviceManager**. Note that the **getTrustedDeviceListSync** API is open only to system applications. The sample code is as follows: +```ts +import deviceManager from '@ohos.distributedHardware.deviceManager'; +var dmClass; +function getRemoteDeviceId() { + if (typeof dmClass === 'object' && dmClass != null) { + var list = dmClass.getTrustedDeviceListSync(); + if (typeof (list) == 'undefined' || typeof (list.length) == 'undefined') { + console.log("MainAbility onButtonClick getRemoteDeviceId err: list is null"); + return; + } + console.log("MainAbility onButtonClick getRemoteDeviceId success:" + list[0].deviceId); + return list[0].deviceId; + } else { + console.log("MainAbility onButtonClick getRemoteDeviceId err: dmClass is null"); + } +} +``` +In the cross-device scenario, the application must also apply for the data synchronization permission from end users. The sample code is as follows: +```ts +let context = this.context +let permissions: Array = ['ohos.permission.DISTRIBUTED_DATASYNC'] +context.requestPermissionsFromUser(permissions).then((data) => { + console.log("Succeed to request permission from user with data: "+ JSON.stringify(data)) +}).catch((error) => { + console.log("Failed to request permission from user with error: "+ JSON.stringify(error)) +}) +``` +3. Send agreed sequenceable data. + +The sequenceable data can be sent to the callee in either of the following ways: without a return value or obtaining data returned by the callee. The method and sequenceable data must be consistent with those of the callee. The following example describes how to invoke the **Call** API to send data to the callee. The sample code is as follows: +```ts +const MSG_SEND_METHOD: string = 'CallSendMsg' +async onButtonCall() { + try { + let msg = new MySequenceable(1, 'origin_Msg') + await this.caller.call(MSG_SEND_METHOD, msg) + } catch (error) { + Logger.error(TAG, `caller call failed with ${error}`) + } +} +``` + +In the following, **CallWithResult** is used to send data **originMsg** to the callee and assign the data processed by the **CallSendMsg** method to **backMsg**. The sample code is as follows: +```ts +const MSG_SEND_METHOD: string = 'CallSendMsg' +originMsg: string = '' +backMsg: string = '' +async onButtonCallWithResult(originMsg, backMsg) { + try { + let msg = new MySequenceable(1, originMsg) + const data = await this.caller.callWithResult(MSG_SEND_METHOD, msg) + Logger.log(TAG, 'caller callWithResult succeed') + + let result = new MySequenceable(0, '') + data.readSequenceable(result) + backMsg(result.str) + Logger.log(TAG, `caller result is [${result.num}, ${result.str}]`) + } catch (error) { + Logger.error(TAG, `caller callWithResult failed with ${error}`) + } +} +``` +4. Release the caller interface. + +When the caller interface is no longer required, call the **release** API to release it. The sample code is as follows: +```ts +try { + this.caller.release() + this.caller = undefined + Logger.log(TAG, 'caller release succeed') +} catch (error) { + Logger.error(TAG, `caller release failed with ${error}`) +} +``` + +## Development Example +The following sample is provided to help you better understand how to develop an ability call in the stage model: + +[eTSStageCallAbility](https://gitee.com/openharmony/app_samples/tree/master/ability/eTSStageCallAbility) + +In this sample, the **AbilityStage** APIs are implemented in the **AbilityStage.ts** file in the **Application** directory, the **Ability** APIs are implemented in the **MainAbility** directory, and **pages/index** is the pages of the ability. Another ability and callee are implemented in the **CalleeAbility** directory, and its pages are the content configured in **pages/second**. The **MainAbility** functions as the caller, and the **CalleeAbility** functions as the callee. After starting the **CalleeAbility**, the **MainAbility** obtains the caller interface, processes the string entered by the user, and transfers the processed string to the **CalleeAbility**. The **CalleeAbility** refreshes the page based on the received data and returns the result to the **MainAbility**. -- GitLab