未验证 提交 4c10e51d 编写于 作者: O openharmony_ci 提交者: Gitee

!2685 Done! 2259:Add stage-ability.md and stage-call.md

Merge pull request !2685 from wusongqing/TR2259
......@@ -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)
# 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<void>)|Starts an ability.|
|void startAbility(want: Want, options: StartOptions, callback: AsyncCallback<void>)|Starts an ability with start options.|
|void startAbilityWithAccount(want: Want, accountId: number, callback: AsyncCallback<void>)|Starts an ability with the account ID.|
|void startAbilityWithAccount(want: Want, accountId: number, options: StartOptions, callback: AsyncCallback<void>)|Starts an ability with the account ID and start options.|
|void startAbilityForResult(want: Want, callback: AsyncCallback<AbilityResult>)|Starts an ability with the returned result.|
|void startAbilityForResult(want: Want, options: StartOptions, callback: AsyncCallback<AbilityResult>)|Starts an ability with the returned result and start options.|
|void startAbilityForResultWithAccount(want: Want, accountId: number, callback: AsyncCallback<AbilityResult>)|Starts an ability with the returned result and account ID.|
|void startAbilityForResultWithAccount(want: Want, accountId: number, options: StartOptions, callback: AsyncCallback<void>)|Starts an ability with the returned result, account ID, and start options.|
|void terminateSelf(callback: AsyncCallback<void>)|Destroys the Page ability.|
|void terminateSelfWithResult(parameter: AbilityResult, callback: AsyncCallback<void>)|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<string> = ['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<string> = ['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.
# 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<Caller> 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<void> call(method: string, data: rpc.Sequenceable)|Caller.call: sends agreed sequenceable data to the callee.|
|Promise<rpc.MessageParcel> 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<string> = ['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**.
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册