提交 71c6fac0 编写于 作者: E ester.zhou 提交者: Gitee

Merge branch 'master' of gitee.com:openharmony/docs into TR-18192

Signed-off-by: Nester.zhou <ester.zhou@huawei.com>
......@@ -12,10 +12,13 @@ The **AccessibilityExtensionAbility** module provides accessibility extension ca
This document is organized as follows:
- [Creating an AccessibilityExtAbility File](#creating-an-accessibility-extension-service)
- [Processing an Accessibility Event](#processing-an-accessibility-event)
- [Declaring Capabilities of Accessibility Extension Services](#declaring-capabilities-of-accessibility-extension-services)
- [Enabling a Custom Accessibility Extension Service](#enabling-a-custom-accessibility-extension-service)
- [AccessibilityExtensionAbility Development](#accessibilityextensionability-development)
- [Creating an Accessibility Extension Service](#creating-an-accessibility-extension-service)
- [Creating a Project](#creating-a-project)
- [Creating an AccessibilityExtAbility File](#creating-an-accessibilityextability-file)
- [Processing an Accessibility Event](#processing-an-accessibility-event)
- [Declaring Capabilities of Accessibility Extension Services](#declaring-capabilities-of-accessibility-extension-services)
- [Enabling a Custom Accessibility Extension Service](#enabling-a-custom-accessibility-extension-service)
## Creating an Accessibility Extension Service
......@@ -79,13 +82,13 @@ You can also process physical key events in the accessibility extension service.
## Declaring Capabilities of Accessibility Extension Services
After developing the custom logic for an accessibility extension service, you must add the configuration information of the service to the corresponding module-level **module.json5** file in the project directory. In the file, the **srcEntrance** tag indicates the path to the accessibility extension service. Make sure the value of the **type** tag is fixed at **accessibility**. Otherwise, the connection to the service will fail.
After developing the custom logic for an accessibility extension service, you must add the configuration information of the service to the corresponding module-level **module.json5** file in the project directory. In the file, the **srcEntry** tag indicates the path to the accessibility extension service. Make sure the value of the **type** tag is fixed at **accessibility**. Otherwise, the connection to the service will fail.
```json
"extensionAbilities": [
{
"name": "AccessibilityExtAbility",
"srcEntrance": "./ets/AccessibilityExtAbility/AccessibilityExtAbility.ts",
"srcEntry": "./ets/AccessibilityExtAbility/AccessibilityExtAbility.ts",
"label": "$string:MainAbility_label",
"description": "$string:MainAbility_desc",
"type": "accessibility",
......
......@@ -22,8 +22,6 @@ A static subscriber is started once it receives a target event published by the
You can implement service logic in the **onReceiveEvent** callback.
2. Project Configuration for a Static Subscriber
After writing the static subscriber code, configure the subscriber in the **module.json5** file. The configuration format is as follows:
......@@ -35,12 +33,12 @@ A static subscriber is started once it receives a target event published by the
"extensionAbilities": [
{
"name": "StaticSubscriber",
"srcEntrance": "./ets/StaticSubscriber/StaticSubscriber.ts",
"srcEntry": "./ets/StaticSubscriber/StaticSubscriber.ts",
"description": "$string:StaticSubscriber_desc",
"icon": "$media:icon",
"label": "$string:StaticSubscriber_label",
"type": "staticSubscriber",
"visible": true,
"exported": true,
"metadata": [
{
"name": "ohos.extension.staticSubscriber",
......@@ -56,7 +54,7 @@ A static subscriber is started once it receives a target event published by the
Pay attention to the following fields in the JSON file:
- **srcEntrance**: entry file path of the ExtensionAbility, that is, the file path of the static subscriber declared in Step 2.
- **srcEntry**: entry file path of the ExtensionAbility, that is, the file path of the static subscriber declared in Step 2.
- **type**: ExtensionAbility type. For a static subscriber, set this field to **staticSubscriber**.
......
......@@ -60,15 +60,15 @@ To implement EnterpriseAdminExtensionAbility, you need to activate the device ad
};
```
4. Register **ServiceExtensionAbility** in the [**module.json5**](../quick-start/module-configuration-file.md) file corresponding to the project module. Set **type** to **enterpriseAdmin** and **srcEntrance** to the path of the ExtensionAbility code.
4. Register **ServiceExtensionAbility** in the [**module.json5**](../quick-start/module-configuration-file.md) file corresponding to the project module. Set **type** to **enterpriseAdmin** and **srcEntry** to the path of the ExtensionAbility code.
```ts
"extensionAbilities": [
{
"name": "ohos.samples.enterprise_admin_ext_ability",
"type": "enterpriseAdmin",
"visible": true,
"srcEntrance": "./ets/enterpriseextability/EnterpriseAdminAbility.ts"
"exported": true,
"srcEntry": "./ets/enterpriseextability/EnterpriseAdminAbility.ts"
}
]
```
......
......@@ -54,7 +54,7 @@ The minimum template contains four files: **KeyboardController.ts**, **InputMeth
1. **InputMethodService.ts** file:
In this file, add the dependency package for importing InputMethodExtensionAbility. Customize a class that inherits from InputMethodExtensionAbility and add the required lifecycle callbacks.
In the **InputMethodService.ts** file, add the dependency package for importing InputMethodExtensionAbility. Customize a class that inherits from InputMethodExtensionAbility and add the required lifecycle callbacks.
```ts
import InputMethodExtensionAbility from '@ohos.InputMethodExtensionAbility';
......@@ -233,7 +233,7 @@ The minimum template contains four files: **KeyboardController.ts**, **InputMeth
Add the path to this file to the **src** field in the **resources/base/profile/main_pages.json** file.
```ets
```ts
import { numberSourceListData, sourceListType } from './keyboardKeyData'
@Component
......@@ -342,7 +342,7 @@ The minimum template contains four files: **KeyboardController.ts**, **InputMeth
}
```
Register the InputMethodExtensionAbility in the [module.json5 file](../quick-start/module-configuration-file.md) corresponding to the target module. Set **type** to **"inputMethod"** and **srcEntrance** to the code path of the InputMethodExtensionAbility component.
Register the InputMethodExtensionAbility in the [module.json5 file](../quick-start/module-configuration-file.md) corresponding to the target module. Set **type** to **"inputMethod"** and **srcEntry** to the code path of the InputMethodExtensionAbility component.
```ts
{
......@@ -353,9 +353,9 @@ The minimum template contains four files: **KeyboardController.ts**, **InputMeth
"description": "inputMethod",
"icon": "$media:icon",
"name": "InputMethodExtAbility",
"srcEntrance": "./ets/inputmethodextability/InputMethodService.ts",
"srcEntry": "./ets/inputmethodextability/InputMethodService.ts",
"type": "inputMethod",
"visible": true,
"exported": true,
}
]
}
......
......@@ -44,7 +44,6 @@ Note that you must set the **bundleType** field to **atomicService** in the [app
"versionName": "1.0.0",
"icon": "$media:app_icon",
"label": "$string:app_name",
"distributedNotificationEnabled": true,
"targetAPIVersion": 9
}
}
......@@ -78,7 +77,7 @@ Pre-loading is triggered after the first frame of the newly accessed module is r
"module": {
"name": "entry",
"type": "entry",
"srcEntrance": "./ets/Application/MyAbilityStage.ts",
"srcEntry": "./ets/Application/MyAbilityStage.ts",
"description": "$string:entry_desc",
"mainElement": "MainAbility",
"deviceTypes": [
......@@ -98,13 +97,13 @@ Pre-loading is triggered after the first frame of the newly accessed module is r
"abilities": [
{
"name": "MainAbility",
"srcEntrance": "./ets/MainAbility/MainAbility.ts",
"srcEntry": "./ets/MainAbility/MainAbility.ts",
"description": "$string:MainAbility_desc",
"icon": "$media:icon",
"label": "$string:MainAbility_label",
"startWindowIcon": "$media:icon",
"startWindowBackground": "$color:white",
"visible": true,
"exported": true,
"skills": [
{
"entities": [
......@@ -170,7 +169,7 @@ import router from '@ohos.router';
@Entry
@Component
struct Index {
@State message: string = 'Hello World'
@State message: string = 'Hello World';
build() {
Row() {
......@@ -200,19 +199,20 @@ struct Index {
}).catch(err => {
console.error(`pushUrl failed, code is ${err.code}, message is ${err.message}`);
})
}
.width('100%')
})
.width('100%')
}
.height('100%')
}
}
}
```
The input parameter **url** of the **router.pushUrl** API is as follows:
```ets
```ts
'@bundle:com.example.hmservice/library/ets/pages/menu'
```
The **url** content template is as follows:
```ets
```ts
'@bundle:bundle name/module name/path/page file name (without the extension .ets)'
```
# app.js
## Application Lifecycle<sup>4+</sup>
You can implement lifecycle logic specific to your application in the **app.js** file. Available application lifecycle functions are as follows:
......@@ -11,7 +12,9 @@ You can implement lifecycle logic specific to your application in the **app.js**
In the following example, logs are printed only in the lifecycle functions.
```
```js
// app.js
export default {
onCreate() {
......@@ -22,3 +25,63 @@ export default {
},
}
```
## Application Object<sup>10+</sup>
| Attribute | Type | Description |
| ------ | -------- | ---------------------------------------- |
| getApp | Function | Obtains the data object exposed in **app.js** from the page JS file. This API works globally.|
> **NOTE**
>
> The application object is global data and occupies JS memory before the application exits. Although it facilitates data sharing between different pages, exercise caution when using it on small-system devices, whose memory is small. If they are overused, exceptions may occur due to insufficient memory when the application displays complex pages.
The following is an example:
Declare the application object in **app.js**.
```javascript
// app.js
export default {
data: {
test: "by getAPP"
},
onCreate() {
console.info('Application onCreate');
},
onDestroy() {
console.info('Application onDestroy');
},
};
```
Access the application object on a specific page.
```javascript
// index.js
export default {
data: {
title: ""
},
onInit() {
if (typeof getApp !== 'undefined') {
var appData = getApp().data;
if (typeof appData !== 'undefined') {
this.title = appData.name; // read from app data
}
}
},
clickHandler() {
if (typeof getApp !== 'undefined') {
var appData = getApp().data;
if (typeof appData !== 'undefined') {
appData.name = this.title; // write to app data
}
}
}
}
```
> **NOTE**
>
> To ensure that the application can run properly on an earlier version that does not support **getApp**, compatibility processing must be performed in the code. That is, before using **getApp**, check whether it is available.
......@@ -29,7 +29,7 @@ IPC/RPC的主要工作是让运行在不同进程的Proxy和Stub互相通信,
external_deps = [
"ipc:ipc_single",
]
#rpc场景
external_deps = [
"ipc:ipc_core",
......@@ -50,12 +50,12 @@ IPC/RPC的主要工作是让运行在不同进程的Proxy和Stub互相通信,
```c++
#include "iremote_broker.h"
//定义消息码
const int TRANS_ID_PING_ABILITY = 5
const int TRANS_ID_PING_ABILITY = 5;
const std::string DESCRIPTOR = "test.ITestAbility";
class ITestAbility : public IRemoteBroker {
public:
// DECLARE_INTERFACE_DESCRIPTOR是必需的,入参需使用std::u16string;
......@@ -71,13 +71,13 @@ IPC/RPC的主要工作是让运行在不同进程的Proxy和Stub互相通信,
```c++
#include "iability_test.h"
#include "iremote_stub.h"
class TestAbilityStub : public IRemoteStub<ITestAbility> {
public:
virtual int OnRemoteRequest(uint32_t code, MessageParcel &data, MessageParcel &reply, MessageOption &option) override;
int TestPingAbility(const std::u16string &dummy) override;
};
int TestAbilityStub::OnRemoteRequest(uint32_t code,
MessageParcel &data, MessageParcel &reply, MessageOption &option)
{
......@@ -98,12 +98,12 @@ IPC/RPC的主要工作是让运行在不同进程的Proxy和Stub互相通信,
```c++
#include "iability_server_test.h"
class TestAbility : public TestAbilityStub {
public:
int TestPingAbility(const std::u16string &dummy);
}
int TestAbility::TestPingAbility(const std::u16string &dummy) {
return 0;
}
......@@ -117,7 +117,7 @@ IPC/RPC的主要工作是让运行在不同进程的Proxy和Stub互相通信,
#include "iability_test.h"
#include "iremote_proxy.h"
#include "iremote_object.h"
class TestAbilityProxy : public IRemoteProxy<ITestAbility> {
public:
explicit TestAbilityProxy(const sptr<IRemoteObject> &impl);
......@@ -125,12 +125,12 @@ IPC/RPC的主要工作是让运行在不同进程的Proxy和Stub互相通信,
private:
static inline BrokerDelegator<TestAbilityProxy> delegator_; // 方便后续使用iface_cast宏
}
TestAbilityProxy::TestAbilityProxy(const sptr<IRemoteObject> &impl)
: IRemoteProxy<ITestAbility>(impl)
{
}
int TestAbilityProxy::TestPingAbility(const std::u16string &dummy){
MessageOption option;
MessageParcel dataParcel, replyParcel;
......@@ -149,7 +149,7 @@ IPC/RPC的主要工作是让运行在不同进程的Proxy和Stub互相通信,
// 注册到本设备内
auto samgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
samgr->AddSystemAbility(saId, new TestAbility());
// 在组网场景下,会被同步到其他设备上
auto samgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
ISystemAbilityManager::SAExtraProp saExtra;
......@@ -166,10 +166,10 @@ IPC/RPC的主要工作是让运行在不同进程的Proxy和Stub互相通信,
sptr<ISystemAbilityManager> samgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
sptr<IRemoteObject> remoteObject = samgr->GetSystemAbility(saId);
sptr<ITestAbility> testAbility = iface_cast<ITestAbility>(remoteObject); // 使用iface_cast宏转换成具体类型
// 获取其他设备注册的SA的proxy
sptr<ISystemAbilityManager> samgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
// networkId是组网场景下对应设备的标识符,可以通过GetLocalNodeDeviceInfo获取
sptr<IRemoteObject> remoteObject = samgr->GetSystemAbility(saId, networkId);
sptr<TestAbilityProxy> proxy(new TestAbilityProxy(remoteObject)); // 直接构造具体Proxy
......@@ -180,59 +180,97 @@ IPC/RPC的主要工作是让运行在不同进程的Proxy和Stub互相通信,
1. 添加依赖
```ts
import rpc from "@ohos.rpc"
import featureAbility from "@ohos.ability.featureAbility"
import rpc from "@ohos.rpc";
// 仅FA模型需要导入@ohos.ability.featureAbility
// import featureAbility from "@ohos.ability.featureAbility";
```
Stage模型需要获取context
```ts
import Ability from "@ohos.app.ability.UIAbility";
export default class MainAbility extends Ability {
onCreate(want, launchParam) {
console.log("[Demo] MainAbility onCreate");
globalThis.context = this.context;
}
onDestroy() {
console.log("[Demo] MainAbility onDestroy");
}
onWindowStageCreate(windowStage) {
// Main window is created, set main page for this ability
console.log("[Demo] MainAbility onWindowStageCreate");
}
onWindowStageDestroy() {
// Main window is destroyed, release UI related resources
console.log("[Demo] MainAbility onWindowStageDestroy");
}
onForeground() {
// Ability has brought to foreground
console.log("[Demo] MainAbility onForeground");
}
onBackground() {
// Ability has back to background
console.log("[Demo] MainAbility onBackground");
}
}
```
2. 绑定Ability
首先,构造变量want,指定要绑定的Ability所在应用的包名、组件名,如果是跨设备的场景,还需要绑定目标设备NetworkId(组网场景下对应设备的标识符,可以使用deviceManager获取目标设备的NetworkId);然后,构造变量connect,指定绑定成功、绑定失败、断开连接时的回调函数;最后,使用featureAbility提供的接口绑定Ability。
首先,构造变量want,指定要绑定的Ability所在应用的包名、组件名,如果是跨设备的场景,还需要绑定目标设备NetworkId(组网场景下对应设备的标识符,可以使用deviceManager获取目标设备的NetworkId);然后,构造变量connect,指定绑定成功、绑定失败、断开连接时的回调函数;最后,FA模型使用featureAbility提供的接口绑定Ability,Stage模型通过context获取服务后用提供的接口绑定Ability。
```ts
import rpc from "@ohos.rpc"
import featureAbility from "@ohos.ability.featureAbility"
let proxy = null
let connectId = null
import rpc from "@ohos.rpc";
// 仅FA模型需要导入@ohos.ability.featureAbility
// import featureAbility from "@ohos.ability.featureAbility";
let proxy = null;
let connectId = null;
// 单个设备绑定Ability
let want = {
// 包名和组件名写实际的值
"bundleName": "ohos.rpc.test.server",
"abilityName": "ohos.rpc.test.server.ServiceAbility",
}
};
let connect = {
onConnect:function(elementName, remote) {
proxy = remote
proxy = remote;
},
onDisconnect:function(elementName) {
},
onFailed:function() {
proxy = null
proxy = null;
}
}
connectId = featureAbility.connectAbility(want, connect)
};
// FA模型使用此方法连接服务
// connectId = featureAbility.connectAbility(want, connect);
connectId = globalThis.context.connectServiceExtensionAbility(want,connect);
// 如果是跨设备绑定,可以使用deviceManager获取目标设备NetworkId
import deviceManager from '@ohos.distributedHardware.deviceManager'
import deviceManager from '@ohos.distributedHardware.deviceManager';
function deviceManagerCallback(deviceManager) {
let deviceList = deviceManager.getTrustedDeviceListSync()
let networkId = deviceList[0].networkId
let deviceList = deviceManager.getTrustedDeviceListSync();
let networkId = deviceList[0].networkId;
let want = {
"bundleName": "ohos.rpc.test.server",
"abilityName": "ohos.rpc.test.service.ServiceAbility",
"networkId": networkId,
"flags": 256
}
connectId = featureAbility.connectAbility(want, connect)
};
// 建立连接后返回的Id需要保存下来,在断开连接时需要作为参数传入
// FA模型使用此方法连接服务
// connectId = featureAbility.connectAbility(want, connect);
connectId = globalThis.context.connectServiceExtensionAbility(want,connect);
}
// 第一个参数是本应用的包名,第二个参数是接收deviceManager的回调函数
deviceManager.createDeviceManager("ohos.rpc.test", deviceManagerCallback)
deviceManager.createDeviceManager("ohos.rpc.test", deviceManagerCallback);
```
3. 服务端处理客户端请求
......@@ -240,78 +278,80 @@ IPC/RPC的主要工作是让运行在不同进程的Proxy和Stub互相通信,
```ts
onConnect(want: Want) {
var robj:rpc.RemoteObject = new Stub("rpcTestAbility")
return robj
var robj:rpc.RemoteObject = new Stub("rpcTestAbility");
return robj;
}
class Stub extends rpc.RemoteObject {
constructor(descriptor) {
super(descriptor)
super(descriptor);
}
onRemoteMessageRequest(code, data, reply, option) {
// 根据code处理客户端的请求
return true
return true;
}
}
```
4. 客户端处理服务端响应
客户端在onConnect回调里接收到代理对象,调用sendRequestAsync方法发起请求,在期约(JavaScript期约:用于表示一个异步操作的最终完成或失败及其结果值)或者回调函数里接收结果。
客户端在onConnect回调里接收到代理对象,调用sendRequest方法发起请求,在期约(JavaScript期约:用于表示一个异步操作的最终完成或失败及其结果值)或者回调函数里接收结果。
```ts
// 使用期约
let option = new rpc.MessageOption()
let data = rpc.MessageParcel.create()
let reply = rpc.MessageParcel.create()
let option = new rpc.MessageOption();
let data = rpc.MessageParcel.create();
let reply = rpc.MessageParcel.create();
// 往data里写入参数
proxy.sendRequestAsync(1, data, reply, option)
proxy.sendRequest(1, data, reply, option)
.then(function(result) {
if (result.errCode != 0) {
console.error("send request failed, errCode: " + result.errCode)
return
console.error("send request failed, errCode: " + result.errCode);
return;
}
// 从result.reply里读取结果
})
.catch(function(e) {
console.error("send request got exception: " + e)
}
console.error("send request got exception: " + e);
})
.finally(() => {
data.reclaim()
reply.reclaim()
data.reclaim();
reply.reclaim();
})
// 使用回调函数
function sendRequestCallback(result) {
try {
if (result.errCode != 0) {
console.error("send request failed, errCode: " + result.errCode)
return
console.error("send request failed, errCode: " + result.errCode);
return;
}
// 从result.reply里读取结果
} finally {
result.data.reclaim()
result.reply.reclaim()
result.data.reclaim();
result.reply.reclaim();
}
}
let option = new rpc.MessageOption()
let data = rpc.MessageParcel.create()
let reply = rpc.MessageParcel.create()
let option = new rpc.MessageOption();
let data = rpc.MessageParcel.create();
let reply = rpc.MessageParcel.create();
// 往data里写入参数
proxy.sendRequest(1, data, reply, option, sendRequestCallback)
proxy.sendRequest(1, data, reply, option, sendRequestCallback);
```
5. 断开连接
IPC通信结束后,使用featureAbility的接口断开连接。
IPC通信结束后,FA模型使用featureAbility的接口断开连接,Stage模型在获取context后用提供的接口断开连接。
```ts
import rpc from "@ohos.rpc"
import featureAbility from "@ohos.ability.featureAbility"
import rpc from "@ohos.rpc";
// 仅FA模型需要导入@ohos.ability.featureAbility
// import featureAbility from "@ohos.ability.featureAbility";
function disconnectCallback() {
console.info("disconnect ability done")
console.info("disconnect ability done");
}
featureAbility.disconnectAbility(connectId, disconnectCallback)
// FA模型使用此方法断开连接
// featureAbility.disconnectAbility(connectId, disconnectCallback);
globalThis.context.disconnectServiceExtensionAbility(connectId);
```
......@@ -3,35 +3,25 @@
## 基本概念
IPC(Inter-Process Communication)与RPC(Remote Procedure Call)用于实现跨进程通信,不同的是前者使用Binder驱动,用于设备内的跨进程通信,后者使用软总线驱动,用于跨设备跨进程通信。需要跨进程通信的原因是因为每个进程都有自己独立的资源和内存空间,其他进程不能随意访问不同进程的内存和资源,IPC/RPC便是为了突破这一点。IPC和RPC通常采用客户端-服务器(Client-Server)模型,在使用时,请求服务的(Client)一端进程可获取提供服务(Server)一端所在进程的代理(Proxy),并通过此代理读写数据来实现进程间的数据通信,更具体的讲,首先请求服务的(Client)一端会建立一个服务提供端(Server)的代理对象,这个代理对象具备和服务提供端(Server)一样的功能,若想访问服务提供端(Server)中的某一个方法,只需访问代理对象中对应的方法即可,代理对象会将请求发送给服务提供端(Server);然后服务提供端(Server)处理接受到的请求,处理完之后通过驱动返回处理结果给代理对象;最后代理对象将请求结果进一步返回给请求服务端(Client)。通常,Server会先注册系统能力(System Ability)到系统能力管理者(System Ability Manager,缩写SAMgr)中,SAMgr负责管理这些SA并向Client提供相关的接口。Client要和某个具体的SA通信,必须先从SAMgr中获取该SA的代理,然后使用代理和SA通信。下文直接使用Proxy表示服务请求方,Stub表示服务提供方。
IPC(Inter-Process Communication)与RPC(Remote Procedure Call)用于实现跨进程通信,不同的是前者使用Binder驱动,用于设备内的跨进程通信,后者使用软总线驱动,用于跨设备跨进程通信。需要跨进程通信的原因是因为每个进程都有自己独立的资源和内存空间,其他进程不能随意访问不同进程的内存和资源,IPC/RPC便是为了突破这一点。
![IPC&RPC通信机制](figures/075sd302-aeb9-481a-bb8f-e552sdb61ead.PNG)
## 约束与限制
- 单个设备上跨进程通信时,传输的数据量最大约为1MB,过大的数据量请使用[匿名共享内存](https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/reference/apis/js-apis-rpc.md#ashmem8)
- 不支持在RPC中订阅匿名Stub对象(没有向SAMgr注册Stub对象)的死亡通知。
- 不支持把跨设备的Proxy对象传递回该Proxy对象所指向的Stub对象所在的设备,即指向远端设备Stub的Proxy对象不能在本设备内进行二次跨进程传递。
## 使用建议
首先,需要编写接口类,接口类中必须定义消息码,供通信双方标识操作,可以有未实现的的方法,因为通信双方均需继承该接口类且双方不能是抽象类,所以此时定义的未实现的方法必须在双方继承时给出实现,这保证了继承双方不是抽象类。然后,需要编写Stub端相关类及其接口,并且实现AsObject方法及OnRemoteRequest方法。同时,也需要编写Proxy端,实现接口类中的方法和AsObject方法,也可以封装一些额外的方法用于调用SendRequest向对端发送数据。以上三者都具备后,便可以向SAMgr注册SA了,此时的注册应该在Stub所在进程完成。最后,在需要的地方从SAMgr中获取Proxy,便可通过Proxy实现与Stub的跨进程通信了。
> **说明:**
> Stage模型不能直接使用本文介绍的IPC和RPC,需要通过以下能力实现相关业务场景:
>- IPC典型使用场景为[后台服务](https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/application-models/background-services.md),后台服务通过IPC机制提供跨进程的服务调用能力。
>- RPC典型使用场景为[多端协同](https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/application-models/hop-multi-device-collaboration.md),多端协同通过RPC机制提供远端接口调用与数据传递。
相关步骤:
- 实现接口类:需继承IRemoteBroker,需定义消息码,可声明不在此类实现的方法。
## 实现原理
- 实现服务提供端(Stub):需继承IRemoteStub或者RemoteObject,需重写AsObject方法及OnRemoteRequest方法
IPC和RPC通常采用客户端-服务器(Client-Server)模型,在使用时,请求服务的(Client)一端进程可获取提供服务(Server)一端所在进程的代理(Proxy),并通过此代理读写数据来实现进程间的数据通信,更具体的讲,首先请求服务的(Client)一端会建立一个服务提供端(Server)的代理对象,这个代理对象具备和服务提供端(Server)一样的功能,若想访问服务提供端(Server)中的某一个方法,只需访问代理对象中对应的方法即可,代理对象会将请求发送给服务提供端(Server);然后服务提供端(Server)处理接受到的请求,处理完之后通过驱动返回处理结果给代理对象;最后代理对象将请求结果进一步返回给请求服务端(Client)。通常,Server会先注册系统能力(System Ability)到系统能力管理者(System Ability Manager,缩写SAMgr)中,SAMgr负责管理这些SA并向Client提供相关的接口。Client要和某个具体的SA通信,必须先从SAMgr中获取该SA的代理,然后使用代理和SA通信。下文直接使用Proxy表示服务请求方,Stub表示服务提供方
- 实现服务请求端(Proxy):需继承IRemoteProxy或RemoteProxy,需重写AsObject方法,封装所需方法调用SendRequest。
![IPC&RPC通信机制](figures/075sd302-aeb9-481a-bb8f-e552sdb61ead.PNG)
- 注册SA:申请SA的唯一ID,向SAMgr注册SA。
- 获取SA:通过SA的ID和设备ID获取Proxy,使用Proxy与远端通信
## 约束与限制
- 单个设备上跨进程通信时,传输的数据量最大约为1MB,过大的数据量请使用[匿名共享内存](https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/reference/apis/js-apis-rpc.md#ashmem8)
## 相关模块
- 不支持在RPC中订阅匿名Stub对象(没有向SAMgr注册Stub对象)的死亡通知。
[分布式任务调度子系统](https://gitee.com/openharmony/ability_dmsfwk)
- 不支持把跨设备的Proxy对象传递回该Proxy对象所指向的Stub对象所在的设备,即指向远端设备Stub的Proxy对象不能在本设备内进行二次跨进程传递。
\ No newline at end of file
# 远端状态订阅开发实例
IPC/RPC提供对远端Stub对象状态的订阅机制, 在远端Stub对象消亡时,可触发消亡通知告诉本地Proxy对象。这种状态通知订阅需要调用特定接口完成,当不再需要订阅时也需要调用特定接口取消。使用这种订阅机制的用户,需要实现消亡通知接口DeathRecipient并实现onRemoteDied方法清理资源。该方法会在远端Stub对象所在进程消亡或所在设备离开组网时被回调。值得注意的是,调用这些接口有一定的顺序。首先,需要Proxy订阅Stub消亡通知,若在订阅期间Stub状态正常,则在不再需要时取消订阅;若在订阅期间Stub所在进程退出或者所在设备退出组网,则会自动触发Proxy自定义的后续操作。
IPC/RPC提供对远端Stub对象状态的订阅机制,在远端Stub对象消亡时,可触发消亡通知告诉本地Proxy对象。这种状态通知订阅需要调用特定接口完成,当不再需要订阅时也需要调用特定接口取消。使用这种订阅机制的用户,需要实现消亡通知接口DeathRecipient并实现onRemoteDied方法清理资源。该方法会在远端Stub对象所在进程消亡或所在设备离开组网时被回调。值得注意的是,调用这些接口有一定的顺序。首先,需要Proxy订阅Stub消亡通知,若在订阅期间Stub状态正常,则在不再需要时取消订阅;若在订阅期间Stub所在进程退出或者所在设备退出组网,则会自动触发Proxy自定义的后续操作。
## 使用场景
......@@ -21,7 +21,6 @@ IPC/RPC提供对远端Stub对象状态的订阅机制, 在远端Stub对象消
#include "iremote_broker.h"
#include "iremote_stub.h"
//定义消息码
enum {
TRANS_ID_PING_ABILITY = 5,
......@@ -61,9 +60,6 @@ int TestServiceProxy::TestPingAbility(const std::u16string &dummy){
}
```
```c++
#include "iremote_object.h"
......@@ -86,16 +82,52 @@ result = object->RemoveDeathRecipient(deathRecipient); // 移除消亡通知
## JS侧接口
| 接口名 | 返回值类型 | 功能描述 |
| -------------------- | ---------- | ------------------------------------------------------------ |
| addDeathRecippient | boolean | 注册用于接收远程对象消亡通知的回调,增加proxy对象上的消亡通知。 |
| removeDeathRecipient | boolean | 注销用于接收远程对象消亡通知的回调。 |
| onRemoteDied | void | 在成功添加死亡通知订阅后,当远端对象死亡时,将自动调用本方法。 |
| 接口名 | 返回值类型 | 功能描述 |
| ------------------------ | ---------- | ----------------------------------------------------------------- |
| registerDeathRecipient | void | 注册用于接收远程对象消亡通知的回调,增加 proxy 对象上的消亡通知。 |
| unregisterDeathRecipient | void | 注销用于接收远程对象消亡通知的回调。 |
| onRemoteDied | void | 在成功添加死亡通知订阅后,当远端对象死亡时,将自动调用本方法。 |
### 获取context
Stage模型在连接服务前需要先获取context
```ts
import Ability from "@ohos.app.ability.UIAbility";
export default class MainAbility extends Ability {
onCreate(want, launchParam) {
console.log("[Demo] MainAbility onCreate");
globalThis.context = this.context;
}
onDestroy() {
console.log("[Demo] MainAbility onDestroy");
}
onWindowStageCreate(windowStage) {
// Main window is created, set main page for this ability
console.log("[Demo] MainAbility onWindowStageCreate");
}
onWindowStageDestroy() {
// Main window is destroyed, release UI related resources
console.log("[Demo] MainAbility onWindowStageDestroy");
}
onForeground() {
// Ability has brought to foreground
console.log("[Demo] MainAbility onForeground");
}
onBackground() {
// Ability has back to background
console.log("[Demo] MainAbility onBackground");
}
}
```
### 参考代码
```ts
import FA from "@ohos.ability.featureAbility";
// 仅FA模型需要导入@ohos.ability.featureAbility
// import FA from "@ohos.ability.featureAbility";
let proxy;
let connect = {
onConnect: function(elementName, remoteProxy) {
......@@ -113,15 +145,19 @@ let want = {
"bundleName": "com.ohos.server",
"abilityName": "com.ohos.server.EntryAbility",
};
FA.connectAbility(want, connect);
// FA模型通过此方法连接服务
// FA.connectAbility(want, connect);
globalThis.context.connectServiceExtensionAbility(want, connect);
class MyDeathRecipient {
onRemoteDied() {
console.log("server died");
}
}
let deathRecipient = new MyDeathRecipient();
proxy.addDeathRecippient(deathRecipient, 0);
proxy.removeDeathRecipient(deathRecipient, 0);
proxy.registerDeathRecippient(deathRecipient, 0);
proxy.unregisterDeathRecipient(deathRecipient, 0);
```
## Stub感知Proxy消亡(匿名Stub的使用)
......
......@@ -167,4 +167,4 @@ USB设备可作为Host设备连接Device设备进行数据传输。开发示例
针对USB管理开发,有以下相关实例可供参考:
- [`USBManager`:USB管理(ArkTS)(API9)](https://gitee.com/openharmony/applications_app_samples/tree/master/code/BasicFeature/DeviceManagement/USBManager)
\ No newline at end of file
- [`DeviceManagementCollection`:设备管理合集(ArkTS)(API9)](https://gitee.com/openharmony/applications_app_samples/tree/master/code/BasicFeature/DeviceManagement/DeviceManagementCollection)
\ No newline at end of file
......@@ -73,7 +73,7 @@ import abilityAccessCtrl from '@ohos.abilityAccessCtrl';
let atManager = abilityAccessCtrl.createAtManager();
let tokenID = 0; // 系统应用可以通过bundleManager.getApplicationInfo获取,普通应用可以通过bundleManager.getBundleInfoForSelf获取
try {
atManager.checkAccessToken(tokenID, "ohos.permission.GRANT_SENSITIVE_PERMISSIONS").then((data) => {
atManager.checkAccessToken(tokenID, 'ohos.permission.GRANT_SENSITIVE_PERMISSIONS').then((data) => {
console.log(`checkAccessToken success, data->${JSON.stringify(data)}`);
}).catch((err) => {
console.log(`checkAccessToken fail, err->${JSON.stringify(err)}`);
......@@ -117,7 +117,7 @@ verifyAccessTokenSync(tokenID: number, permissionName: Permissions): GrantStatus
```js
let atManager = abilityAccessCtrl.createAtManager();
let tokenID = 0; // 系统应用可以通过bundleManager.getApplicationInfo获取,普通应用可以通过bundleManager.getBundleInfoForSelf获取
let data = atManager.verifyAccessTokenSync(tokenID, "ohos.permission.GRANT_SENSITIVE_PERMISSIONS");
let data = atManager.verifyAccessTokenSync(tokenID, 'ohos.permission.GRANT_SENSITIVE_PERMISSIONS');
console.log(`data->${JSON.stringify(data)}`);
```
......@@ -168,7 +168,7 @@ let atManager = abilityAccessCtrl.createAtManager();
let tokenID = 0; // 系统应用可以通过bundleManager.getApplicationInfo获取,普通应用可以通过bundleManager.getBundleInfoForSelf获取
let permissionFlags = 1;
try {
atManager.grantUserGrantedPermission(tokenID, "ohos.permission.GRANT_SENSITIVE_PERMISSIONS", permissionFlags).then(() => {
atManager.grantUserGrantedPermission(tokenID, 'ohos.permission.GRANT_SENSITIVE_PERMISSIONS', permissionFlags).then(() => {
console.log('grantUserGrantedPermission success');
}).catch((err) => {
console.log(`grantUserGrantedPermission fail, err->${JSON.stringify(err)}`);
......@@ -220,7 +220,7 @@ let atManager = abilityAccessCtrl.createAtManager();
let tokenID = 0; // 系统应用可以通过bundleManager.getApplicationInfo获取,普通应用可以通过bundleManager.getBundleInfoForSelf获取
let permissionFlags = 1;
try {
atManager.grantUserGrantedPermission(tokenID, "ohos.permission.GRANT_SENSITIVE_PERMISSIONS", permissionFlags, (err, data) => {
atManager.grantUserGrantedPermission(tokenID, 'ohos.permission.GRANT_SENSITIVE_PERMISSIONS', permissionFlags, (err, data) => {
if (err) {
console.log(`grantUserGrantedPermission fail, err->${JSON.stringify(err)}`);
} else {
......@@ -279,7 +279,7 @@ let atManager = abilityAccessCtrl.createAtManager();
let tokenID = 0; // 系统应用可以通过bundleManager.getApplicationInfo获取,普通应用可以通过bundleManager.getBundleInfoForSelf获取
let permissionFlags = 1;
try {
atManager.revokeUserGrantedPermission(tokenID, "ohos.permission.GRANT_SENSITIVE_PERMISSIONS", permissionFlags).then(() => {
atManager.revokeUserGrantedPermission(tokenID, 'ohos.permission.GRANT_SENSITIVE_PERMISSIONS', permissionFlags).then(() => {
console.log('revokeUserGrantedPermission success');
}).catch((err) => {
console.log(`revokeUserGrantedPermission fail, err->${JSON.stringify(err)}`);
......@@ -331,7 +331,7 @@ let atManager = abilityAccessCtrl.createAtManager();
let tokenID = 0; // 系统应用可以通过bundleManager.getApplicationInfo获取,普通应用可以通过bundleManager.getBundleInfoForSelf获取
let permissionFlags = 1;
try {
atManager.revokeUserGrantedPermission(tokenID, "ohos.permission.GRANT_SENSITIVE_PERMISSIONS", permissionFlags, (err, data) => {
atManager.revokeUserGrantedPermission(tokenID, 'ohos.permission.GRANT_SENSITIVE_PERMISSIONS', permissionFlags, (err, data) => {
if (err) {
console.log(`revokeUserGrantedPermission fail, err->${JSON.stringify(err)}`);
} else {
......@@ -388,7 +388,7 @@ import abilityAccessCtrl from '@ohos.abilityAccessCtrl';
let atManager = abilityAccessCtrl.createAtManager();
let tokenID = 0; // 系统应用可以通过bundleManager.getApplicationInfo获取,普通应用可以通过bundleManager.getBundleInfoForSelf获取
try {
atManager.getPermissionFlags(tokenID, "ohos.permission.GRANT_SENSITIVE_PERMISSIONS").then((data) => {
atManager.getPermissionFlags(tokenID, 'ohos.permission.GRANT_SENSITIVE_PERMISSIONS').then((data) => {
console.log(`getPermissionFlags success, data->${JSON.stringify(data)}`);
}).catch((err) => {
console.log(`getPermissionFlags fail, err->${JSON.stringify(err)}`);
......@@ -466,10 +466,10 @@ import bundleManager from '@ohos.bundle.bundleManager';
let atManager = abilityAccessCtrl.createAtManager();
let appInfo = bundleManager.getApplicationInfoSync('com.example.myapplication', 0, 100);
let tokenIDList: Array<number> = [appInfo.accessTokenId];
let permissionList: Array<Permissions> = ["ohos.permission.DISTRIBUTED_DATASYNC"];
let permissionList: Array<Permissions> = ['ohos.permission.DISTRIBUTED_DATASYNC'];
try {
atManager.on('permissionStateChange', tokenIDList, permissionList, (data) => {
console.debug("receive permission state change, data:" + JSON.stringify(data));
console.debug('receive permission state change, data:' + JSON.stringify(data));
});
} catch(err) {
console.log(`catch err->${JSON.stringify(err)}`);
......@@ -504,7 +504,7 @@ off(type: 'permissionStateChange', tokenIDList: Array&lt;number&gt;, permissionL
| 错误码ID | 错误信息 |
| -------- | -------- |
| 12100001 | The parameter is invalid. The tokenIDs or permissionNames in the list are all invalid. |
| 12100004 | The interface is not used together with "on". |
| 12100004 | The interface is not used together with 'on'. |
| 12100007 | Service is abnormal. |
| 12100008 | Out of memory. |
......@@ -517,7 +517,7 @@ import bundleManager from '@ohos.bundle.bundleManager';
let atManager = abilityAccessCtrl.createAtManager();
let appInfo = bundleManager.getApplicationInfoSync('com.example.myapplication', 0, 100);
let tokenIDList: Array<number> = [appInfo.accessTokenId];
let permissionList: Array<Permissions> = ["ohos.permission.DISTRIBUTED_DATASYNC"];
let permissionList: Array<Permissions> = ['ohos.permission.DISTRIBUTED_DATASYNC'];
try {
atManager.off('permissionStateChange', tokenIDList, permissionList);
} catch(err) {
......@@ -557,7 +557,7 @@ import abilityAccessCtrl from '@ohos.abilityAccessCtrl';
let atManager = abilityAccessCtrl.createAtManager();
let tokenID = 0; // 系统应用可以通过bundleManager.getApplicationInfo获取,普通应用可以通过bundleManager.getBundleInfoForSelf获取
let promise = atManager.verifyAccessToken(tokenID, "ohos.permission.GRANT_SENSITIVE_PERMISSIONS");
let promise = atManager.verifyAccessToken(tokenID, 'ohos.permission.GRANT_SENSITIVE_PERMISSIONS');
promise.then(data => {
console.log(`promise: data->${JSON.stringify(data)}`);
});
......@@ -598,10 +598,10 @@ requestPermissionsFromUser(context: Context, permissionList: Array&lt;Permission
import abilityAccessCtrl from '@ohos.abilityAccessCtrl';
let atManager = abilityAccessCtrl.createAtManager();
try {
atManager.requestPermissionsFromUser(this.context, ["ohos.permission.CAMERA"], (err, data)=>{
console.info("data:" + JSON.stringify(data));
console.info("data permissions:" + data.permissions);
console.info("data authResults:" + data.authResults);
atManager.requestPermissionsFromUser(this.context, ['ohos.permission.CAMERA'], (err, data)=>{
console.info('data:' + JSON.stringify(data));
console.info('data permissions:' + data.permissions);
console.info('data authResults:' + data.authResults);
});
} catch(err) {
console.log(`catch err->${JSON.stringify(err)}`);
......@@ -649,12 +649,12 @@ requestPermissionsFromUser(context: Context, permissionList: Array&lt;Permission
import abilityAccessCtrl from '@ohos.abilityAccessCtrl';
let atManager = abilityAccessCtrl.createAtManager();
try {
atManager.requestPermissionsFromUser(this.context, ["ohos.permission.CAMERA"]).then((data) => {
console.info("data:" + JSON.stringify(data));
console.info("data permissions:" + data.permissions);
console.info("data authResults:" + data.authResults);
atManager.requestPermissionsFromUser(this.context, ['ohos.permission.CAMERA']).then((data) => {
console.info('data:' + JSON.stringify(data));
console.info('data permissions:' + data.permissions);
console.info('data authResults:' + data.authResults);
}).catch((err) => {
console.info("data:" + JSON.stringify(err));
console.info('data:' + JSON.stringify(err));
})
} catch(err) {
console.log(`catch err->${JSON.stringify(err)}`);
......@@ -693,7 +693,7 @@ import abilityAccessCtrl from '@ohos.abilityAccessCtrl';
let atManager = abilityAccessCtrl.createAtManager();
let tokenID = 0; // 系统应用可以通过bundleManager.getApplicationInfo获取,普通应用可以通过bundleManager.getBundleInfoForSelf获取
let promise = atManager.verifyAccessToken(tokenID, "ohos.permission.GRANT_SENSITIVE_PERMISSIONS");
let promise = atManager.verifyAccessToken(tokenID, 'ohos.permission.GRANT_SENSITIVE_PERMISSIONS');
promise.then(data => {
console.log(`promise: data->${JSON.stringify(data)}`);
});
......@@ -733,7 +733,7 @@ checkAccessTokenSync(tokenID: number, permissionName: Permissions): GrantStatus;
```js
let atManager = abilityAccessCtrl.createAtManager();
let tokenID = 0; // 系统应用可以通过bundleManager.getApplicationInfo获取,普通应用可以通过bundleManager.getBundleInfoForSelf获取
let data = atManager.checkAccessTokenSync(tokenID, "ohos.permission.GRANT_SENSITIVE_PERMISSIONS");
let data = atManager.checkAccessTokenSync(tokenID, 'ohos.permission.GRANT_SENSITIVE_PERMISSIONS');
console.log(`data->${JSON.stringify(data)}`);
```
......
......@@ -197,7 +197,7 @@ import featureAbility from '@ohos.ability.featureAbility';
let context = featureAbility.getContext();
try {
data_preferences.deletePreferences(context, 'mystore', function (err, val) {
data_preferences.deletePreferences(context, 'mystore', function (err) {
if (err) {
console.info("Failed to delete preferences. code =" + err.code + ", message =" + err.message);
return;
......@@ -217,7 +217,7 @@ import UIAbility from '@ohos.app.ability.UIAbility';
class EntryAbility extends UIAbility {
onWindowStageCreate(windowStage) {
try {
data_preferences.deletePreferences(this.context, 'mystore', function (err, val) {
data_preferences.deletePreferences(this.context, 'mystore', function (err) {
if (err) {
console.info("Failed to delete preferences. code =" + err.code + ", message =" + err.message);
return;
......@@ -334,7 +334,7 @@ import featureAbility from '@ohos.ability.featureAbility';
let context = featureAbility.getContext();
try {
data_preferences.removePreferencesFromCache(context, 'mystore', function (err, val) {
data_preferences.removePreferencesFromCache(context, 'mystore', function (err) {
if (err) {
console.info("Failed to remove preferences. code =" + err.code + ", message =" + err.message);
return;
......@@ -354,7 +354,7 @@ import UIAbility from '@ohos.app.ability.UIAbility';
class EntryAbility extends UIAbility {
onWindowStageCreate(windowStage) {
try {
data_preferences.removePreferencesFromCache(this.context, 'mystore', function (err, val) {
data_preferences.removePreferencesFromCache(this.context, 'mystore', function (err) {
if (err) {
console.info("Failed to remove preferences. code =" + err.code + ", message =" + err.message);
return;
......
......@@ -127,3 +127,5 @@
- [热场景定制开发指导](subsys-thermal_scene.md)
- 电源管理
- [电源模式定制开发指导](subsys-power-mode-customization.md)
- [电源默认休眠行为定制开发指导](subsys-power-default-sleep-behavior-customization.md)
- [唤醒源定制开发指导](subsys-power-wakeup-source-customization.md)
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册