HarmonyOS 应用开发之多端协同

多端协同流程

多端协同流程如下图所示。

图1 多端协同流程图

约束限制

  • 由于“多端协同任务管理”能力尚未具备,开发者当前只能通过开发系统应用获取设备列表,不支持三方应用接入。

  • 多端协同需遵循 分布式跨设备组件启动规则。

  • 为了获得最佳体验,使用want传输的数据建议在100KB以下。

通过跨设备启动UIAbility和ServiceExtensionAbility组件实现多端协同(无返回数据)

在设备A上通过发起端应用提供的启动按钮,启动设备B上指定的UIAbility与ServiceExtensionAbility。

接口说明

表1 跨设备启动API接口功能介绍

接口名描述
startAbility(want: Want, callback: AsyncCallback): void;启动UIAbility和ServiceExtensionAbility(callback形式)。
stopServiceExtensionAbility(want: Want, callback: AsyncCallback): void;退出启动的ServiceExtensionAbility(callback形式)。
stopServiceExtensionAbility(want: Want): Promise;退出启动的ServiceExtensionAbility(Promise形式)。

开发步骤

  1. 需要申请ohos.permission.DISTRIBUTED_DATASYNC权限,配置方式请参见 声明权限 。

  2. 同时需要在应用首次启动时弹窗向用户申请授权,使用方式请参见 向用户申请授权 。

  3. 获取目标设备的设备ID。

import deviceManager from '@ohos.distributedDeviceManager';
import hilog from '@ohos.hilog';const TAG: string = '[Page_CollaborateAbility]';
const DOMAIN_NUMBER: number = 0xFF00;let dmClass: deviceManager.DeviceManager;function initDmClass(): void {// 其中createDeviceManager接口为系统APItry {dmClass = deviceManager.createDeviceManager('com.samples.stagemodelabilitydevelop');hilog.info(DOMAIN_NUMBER, TAG, JSON.stringify(dmClass) ?? '');} catch (err) {hilog.error(DOMAIN_NUMBER, TAG, 'createDeviceManager err: ' + JSON.stringify(err));};
}function getRemoteDeviceId(): string | undefined {if (typeof dmClass === 'object' && dmClass !== null) {let list = dmClass.getAvailableDeviceListSync();hilog.info(DOMAIN_NUMBER, TAG, JSON.stringify(dmClass), JSON.stringify(list));if (typeof (list) === 'undefined' || typeof (list.length) === 'undefined') {hilog.info(DOMAIN_NUMBER, TAG, 'getRemoteDeviceId err: list is null');return;}if (list.length === 0) {hilog.info(DOMAIN_NUMBER, TAG, `getRemoteDeviceId err: list is empty`);return;}return list[0].networkId;} else {hilog.info(DOMAIN_NUMBER, TAG, 'getRemoteDeviceId err: dmClass is null');return;}
}
  1. 设置目标组件参数,调用 startAbility() 接口,启动UIAbility或ServiceExtensionAbility。
import { BusinessError } from '@ohos.base';
import hilog from '@ohos.hilog';
import Want from '@ohos.app.ability.Want';
import deviceManager from '@ohos.distributedDeviceManager';
import common from '@ohos.app.ability.common';const TAG: string = '[Page_CollaborateAbility]';
const DOMAIN_NUMBER: number = 0xFF00;
let dmClass: deviceManager.DeviceManager;function getRemoteDeviceId(): string | undefined {if (typeof dmClass === 'object' && dmClass !== null) {let list = dmClass.getAvailableDeviceListSync();hilog.info(DOMAIN_NUMBER, TAG, JSON.stringify(dmClass), JSON.stringify(list));if (typeof (list) === 'undefined' || typeof (list.length) === 'undefined') {hilog.info(DOMAIN_NUMBER, TAG, 'getRemoteDeviceId err: list is null');return;}if (list.length === 0) {hilog.info(DOMAIN_NUMBER, TAG, `getRemoteDeviceId err: list is empty`);return;}return list[0].networkId;} else {hilog.info(DOMAIN_NUMBER, TAG, 'getRemoteDeviceId err: dmClass is null');return;}
};@Entry
@Component
struct Page_CollaborateAbility {private context = getContext(this) as common.UIAbilityContext;build() {// ...Button('startAbility').onClick(() => {let want: Want = {deviceId: getRemoteDeviceId(),bundleName: 'com.samples.stagemodelabilityinteraction',abilityName: 'CollaborateAbility',moduleName: 'entry' // moduleName非必选}// context为发起端UIAbility的AbilityContextthis.context.startAbility(want).then(() => {// ...}).catch((err: BusinessError) => {// ...hilog.error(DOMAIN_NUMBER, TAG, `startAbility err: ` + JSON.stringify(err));});})}
}
  1. 当设备A发起端应用不需要设备B上的ServiceExtensionAbility时,可调用 stopServiceExtensionAbility 接口退出。(该接口不支持UIAbility的退出,UIAbility由用户手动通过任务管理退出)
import Want from '@ohos.app.ability.Want';
import hilog from '@ohos.hilog';
import { BusinessError } from '@ohos.base';
import deviceManager from '@ohos.distributedDeviceManager';
import common from '@ohos.app.ability.common';const TAG: string = '[Page_CollaborateAbility]';
const DOMAIN_NUMBER: number = 0xFF00;
let dmClass: deviceManager.DeviceManager;function getRemoteDeviceId(): string | undefined {if (typeof dmClass === 'object' && dmClass !== null) {let list = dmClass.getAvailableDeviceListSync();hilog.info(DOMAIN_NUMBER, TAG, JSON.stringify(dmClass), JSON.stringify(list));if (typeof (list) === 'undefined' || typeof (list.length) === 'undefined') {hilog.info(DOMAIN_NUMBER, TAG, 'getRemoteDeviceId err: list is null');return;}if (list.length === 0) {hilog.info(DOMAIN_NUMBER, TAG, `getRemoteDeviceId err: list is empty`);return;}return list[0].networkId;} else {hilog.info(DOMAIN_NUMBER, TAG, 'getRemoteDeviceId err: dmClass is null');return;}
};@Entry
@Component
struct Page_CollaborateAbility {private context = getContext(this) as common.UIAbilityContext;build() {// ...Button('stopServiceExtensionAbility').onClick(() => {let want: Want = {deviceId: getRemoteDeviceId(),bundleName: 'com.example.myapplication',abilityName: 'FuncAbility',moduleName: 'module1', // moduleName非必选}// 退出由startAbility接口启动的ServiceExtensionAbilitythis.context.stopServiceExtensionAbility(want).then(() => {console.info("stop service extension ability success")}).catch((err: BusinessError) => {console.info("stop service extension ability err is " + JSON.stringify(err))})})}
}

通过跨设备启动UIAbility组件实现多端协同(获取返回数据)

在设备A上通过应用提供的启动按钮,启动设备B上指定的UIAbility,当设备B上的UIAbility退出后,会将返回值发回设备A上的发起端应用。

接口说明

表2 跨设备启动,返回结果数据API接口功能描述

接口名描述
startAbilityForResult(want: Want, callback: AsyncCallback): void;启动UIAbility并在该Ability退出的时候返回执行结果(callback形式)。
terminateSelfWithResult(parameter: AbilityResult, callback: AsyncCallback): void;停止UIAbility,配合startAbilityForResult使用,返回给接口调用方AbilityResult信息(callback形式)。
terminateSelfWithResult(parameter: AbilityResult): Promise;停止UIAbility,配合startAbilityForResult使用,返回给接口调用方AbilityResult信息(promise形式)。

开发步骤

  1. 需要申请ohos.permission.DISTRIBUTED_DATASYNC权限,配置方式请参见 声明权限 。

  2. 同时需要在应用首次启动时弹窗向用户申请授权,使用方式请参见 向用户申请授权 。

  3. 在发起端设置目标组件参数,调用startAbilityForResult()接口启动目标端UIAbility,异步回调中的data用于接收目标端UIAbility停止自身后返回给调用方UIAbility的信息。getRemoteDeviceId方法参照 通过跨设备启动uiability和serviceextensionability组件实现多端协同无返回数据 。

import common from '@ohos.app.ability.common';
import hilog from '@ohos.hilog';
import { BusinessError } from '@ohos.base';
import Want from '@ohos.app.ability.Want';
import deviceManager from '@ohos.distributedDeviceManager';const DOMAIN_NUMBER: number = 0xFF00;
const TAG: string = '[Page_CollaborateAbility]';
let dmClass: deviceManager.DeviceManager;function getRemoteDeviceId(): string | undefined {if (typeof dmClass === 'object' && dmClass !== null) {let list = dmClass.getAvailableDeviceListSync();hilog.info(DOMAIN_NUMBER, TAG, JSON.stringify(dmClass), JSON.stringify(list));if (typeof (list) === 'undefined' || typeof (list.length) === 'undefined') {hilog.info(DOMAIN_NUMBER, TAG, 'getRemoteDeviceId err: list is null');return;}if (list.length === 0) {hilog.info(DOMAIN_NUMBER, TAG, `getRemoteDeviceId err: list is empty`);return;}return list[0].networkId;} else {hilog.info(DOMAIN_NUMBER, TAG, 'getRemoteDeviceId err: dmClass is null');return;}
};@Entry
@Component
struct Page_CollaborateAbility {private context = getContext(this) as common.UIAbilityContext;build() {// ...Button('多端协同有返回数据').onClick(()=>{let want: Want = {deviceId: getRemoteDeviceId(),bundleName: 'com.samples.stagemodelabilityinteraction',abilityName: 'CollaborateAbility',moduleName: 'entry' // moduleName非必选};// context为发起端UIAbility的AbilityContextthis.context.startAbilityForResult(want).then((data) => {// ...}).catch((error: BusinessError) => {hilog.error(DOMAIN_NUMBER, TAG, `startAbilityForResult err: ` + JSON.stringify(error));})})}
}
  1. 在目标端UIAbility任务完成后,调用terminateSelfWithResult()方法,将数据返回给发起端的UIAbility。
import common from '@ohos.app.ability.common';
import hilog from '@ohos.hilog';
import { BusinessError } from '@ohos.base';const DOMAIN_NUMBER: number = 0xFF00;
const TAG: string = '[Page_CollaborateAbility]';@Entry
@Component
struct PageName {private context = getContext(this) as common.UIAbilityContext;build() {// ...Button('关闭多设备协同界面并返回数据').onClick(()=>{const RESULT_CODE: number = 1001;// context为目标端UIAbility的AbilityContextthis.context.terminateSelfWithResult({resultCode: RESULT_CODE,want: {bundleName: 'ohos.samples.stagemodelabilitydevelop',abilityName: 'CollaborateAbility',moduleName: 'entry',parameters: {info: '来自Page_CollaborateAbility页面'}}},(err: BusinessError) => {hilog.info(DOMAIN_NUMBER, TAG, `terminateSelfWithResult err: ` + JSON.stringify(err));});})}
}
  1. 发起端UIAbility接收到目标端UIAbility返回的信息,对其进行处理。
import common from '@ohos.app.ability.common';
import deviceManager from '@ohos.distributedDeviceManager';
import hilog from '@ohos.hilog';
import Want from '@ohos.app.ability.Want';
import { BusinessError } from '@ohos.base';const TAG: string = '[Page_CollaborateAbility]';
const DOMAIN_NUMBER: number = 0xFF00;
let dmClass: deviceManager.DeviceManager;function getRemoteDeviceId(): string | undefined {if (typeof dmClass === 'object' && dmClass !== null) {let list = dmClass.getAvailableDeviceListSync();hilog.info(DOMAIN_NUMBER, TAG, JSON.stringify(dmClass), JSON.stringify(list));if (typeof (list) === 'undefined' || typeof (list.length) === 'undefined') {hilog.info(DOMAIN_NUMBER, TAG, 'getRemoteDeviceId err: list is null');return;}if (list.length === 0) {hilog.info(DOMAIN_NUMBER, TAG, `getRemoteDeviceId err: list is empty`);return;}return list[0].networkId;} else {hilog.info(DOMAIN_NUMBER, TAG, 'getRemoteDeviceId err: dmClass is null');return;}
};@Entry
@Component
struct PageName {private context = getContext(this) as common.UIAbilityContext;build() {// ...Button('多端协同有返回数据').onClick(() => {let want: Want = {deviceId: getRemoteDeviceId(),bundleName: 'com.samples.stagemodelabilityinteraction',abilityName: 'CollaborateAbility',moduleName: 'entry' // moduleName非必选};const RESULT_CODE: number = 1001;// ...// context为调用方UIAbility的UIAbilityContextthis.context.startAbilityForResult(want).then((data) => {if (data?.resultCode === RESULT_CODE) {// 解析目标端UIAbility返回的信息let info = data.want?.parameters?.info;// ...}}).catch((error: BusinessError) => {// ...})})}
}

通过跨设备连接ServiceExtensionAbility组件实现多端协同

系统应用可以通过 connectServiceExtensionAbility() 跨设备连接一个服务,实现跨设备远程调用。比如:分布式游戏场景,平板作为遥控器,智慧屏作为显示器。

接口说明

表3 跨设备连接API接口功能介绍

接口名描述
connectServiceExtensionAbility(want: Want, options: ConnectOptions): number;连接ServiceExtensionAbility。
disconnectServiceExtensionAbility(connection: number, callback:AsyncCallback): void;断开连接(callback形式)。
disconnectServiceExtensionAbility(connection: number): Promise;断开连接(promise形式)。

开发步骤

  1. 需要申请ohos.permission.DISTRIBUTED_DATASYNC权限,配置方式请参见声明权限。

  2. 同时需要在应用首次启动时弹窗向用户申请授权,使用方式请参见 向用户申请授权 。

  3. 如果已有后台服务,请直接进入下一步;如果没有,则 实现一个后台服务(仅对系统应用开放) 。

  4. 连接一个后台服务。

    • 实现IAbilityConnection接口。IAbilityConnection提供了以下方法供开发者实现:onConnect()是用来处理连接Service成功的回调,onDisconnect()是用来处理Service异常终止的回调,onFailed()是用来处理连接Service失败的回调。
    • 设置目标组件参数,包括目标设备ID、Bundle名称、Ability名称。
    • 调用connectServiceExtensionAbility发起连接。
    • 连接成功,收到目标设备返回的服务句柄。
    • 进行跨设备调用,获得目标端服务返回的结果。
import common from '@ohos.app.ability.common';
import deviceManager from '@ohos.distributedDeviceManager';
import hilog from '@ohos.hilog';
import rpc from '@ohos.rpc';
import Want from '@ohos.app.ability.Want';
import { BusinessError } from '@ohos.base';const TAG: string = '[Page_CollaborateAbility]';
const DOMAIN_NUMBER: number = 0xFF00;
const REQUEST_CODE = 1;
let dmClass: deviceManager.DeviceManager;
let connectionId: number;
let options: common.ConnectOptions = {onConnect(elementName, remote) {hilog.info(DOMAIN_NUMBER, TAG, 'onConnect callback');if (remote === null) {hilog.info(DOMAIN_NUMBER, TAG, `onConnect remote is null`);return;}let option = new rpc.MessageOption();let data = new rpc.MessageSequence();let reply = new rpc.MessageSequence();data.writeInt(99); // 开发者可发送data到目标端应用进行相应操作// @param code 表示客户端发送的服务请求代码。// @param data 表示客户端发送的{@link MessageSequence}对象。// @param reply 表示远程服务发送的响应消息对象。// @param options 指示操作是同步的还是异步的。//// @return 如果操作成功返回{@code true}; 否则返回 {@code false}。remote.sendMessageRequest(REQUEST_CODE, data, reply, option).then((ret: rpc.RequestResult) => {let errCode = reply.readInt(); // 在成功连接的情况下,会收到来自目标端返回的信息(100)let msg: number = 0;if (errCode === 0) {msg = reply.readInt();}// 成功连接后台服务hilog.info(DOMAIN_NUMBER, TAG, `sendRequest msg:${msg}`);}).catch((error: BusinessError) => {hilog.info(DOMAIN_NUMBER, TAG, `sendRequest failed, ${JSON.stringify(error)}`);});},onDisconnect(elementName) {hilog.info(DOMAIN_NUMBER, TAG, 'onDisconnect callback');},onFailed(code) {hilog.info(DOMAIN_NUMBER, TAG, 'onFailed callback');}
};function getRemoteDeviceId(): string | undefined {if (typeof dmClass === 'object' && dmClass !== null) {let list = dmClass.getAvailableDeviceListSync();hilog.info(DOMAIN_NUMBER, TAG, JSON.stringify(dmClass), JSON.stringify(list));if (typeof (list) === 'undefined' || typeof (list.length) === 'undefined') {hilog.info(DOMAIN_NUMBER, TAG, 'getRemoteDeviceId err: list is null');return;}if (list.length === 0) {hilog.info(DOMAIN_NUMBER, TAG, `getRemoteDeviceId err: list is empty`);return;}return list[0].networkId;} else {hilog.info(DOMAIN_NUMBER, TAG, 'getRemoteDeviceId err: dmClass is null');return;}
}@Entry
@Component
struct PageName {private context = getContext(this) as common.UIAbilityContext;build() {// ...Button('connectServiceExtensionAbility').onClick(()=>{let want: Want = {'deviceId': getRemoteDeviceId(),'bundleName': 'com.samples.stagemodelabilityinteraction','abilityName': 'ServiceExtAbility'};// 建立连接后返回的Id需要保存下来,在解绑服务时需要作为参数传入connectionId = this.context.connectServiceExtensionAbility(want, options);})}
}

getRemoteDeviceId方法参照 通过跨设备启动uiability和serviceextensionability组件实现多端协同无返回数据 。

  1. 断开连接。调用disconnectServiceExtensionAbility()断开与后台服务的连接。
import common from '@ohos.app.ability.common';
import { BusinessError } from '@ohos.base';
import hilog from '@ohos.hilog';
import Want from '@ohos.app.ability.Want';
import rpc from '@ohos.rpc';
import IdlServiceExtProxy from '../IdlServiceExt/idl_service_ext_proxy';let connectionId: number;
const TAG: string = '[Page_ServiceExtensionAbility]';
const DOMAIN_NUMBER: number = 0xFF00;
let want: Want = {deviceId: '',bundleName: 'com.samples.stagemodelabilitydevelop',abilityName: 'ServiceExtAbility'
};let options: common.ConnectOptions = {onConnect(elementName, remote: rpc.IRemoteObject): void {hilog.info(DOMAIN_NUMBER, TAG, 'onConnect callback');if (remote === null) {hilog.info(DOMAIN_NUMBER, TAG, 'onConnect remote is null');return;}let serviceExtProxy: IdlServiceExtProxy = new IdlServiceExtProxy(remote);// 通过接口调用的方式进行通信,屏蔽了RPC通信的细节,简洁明了serviceExtProxy.processData(1, (errorCode: number, retVal: number) => {hilog.info(DOMAIN_NUMBER, TAG, `processData, errorCode: ${errorCode}, retVal: ${retVal}`);});serviceExtProxy.insertDataToMap('theKey', 1, (errorCode: number) => {hilog.info(DOMAIN_NUMBER, TAG, `insertDataToMap, errorCode: ${errorCode}`);})},onDisconnect(elementName): void {hilog.info(DOMAIN_NUMBER, TAG, 'onDisconnect callback');},onFailed(code: number): void {hilog.info(DOMAIN_NUMBER, TAG, 'onFailed callback', JSON.stringify(code));}
};@Entry
@Component
struct PageName {private context = getContext(this) as common.UIAbilityContext;build() {// ...Button('disconnectServiceExtensionAbility').onClick(() => {this.context.disconnectServiceExtensionAbility(connectionId).then(() => {connectionId = this.context.connectServiceExtensionAbility(want, options);hilog.info(DOMAIN_NUMBER, TAG, 'disconnectServiceExtensionAbility success');// 成功断连后台服务}).catch((error: BusinessError) => {hilog.error(DOMAIN_NUMBER, TAG, 'disconnectServiceExtensionAbility failed');})})}
}

通过跨设备Call调用实现多端协同

跨设备Call调用的基本原理与设备内Call调用相同,请参见通过Call调用实现UIAbility交互(仅对系统应用开放) 。

下面介绍跨设备Call调用实现多端协同的方法。

接口说明

表4 Call API接口功能介绍

接口名描述
startAbilityByCall(want: Want): Promise;启动指定UIAbility至前台或后台,同时获取其Caller通信接口,调用方可使用Caller与被启动的Ability进行通信。
on(method: string, callback: CalleeCallBack): void通用组件Callee注册method对应的callback方法。
off(method: string): void通用组件Callee解注册method的callback方法。
call(method: string, data: rpc.Parcelable): Promise向通用组件Callee发送约定序列化数据。
callWithResult(method: string, data: rpc.Parcelable): Promise<rpc.MessageSequence>向通用组件Callee发送约定序列化数据, 并将Callee返回的约定序列化数据带回。
release(): void释放通用组件的Caller通信接口。
on(type: “release”, callback: OnReleaseCallback): void注册通用组件通信断开监听通知。

开发步骤

  1. 需要申请ohos.permission.DISTRIBUTED_DATASYNC权限,配置方式请参见 声明权限 。

  2. 同时需要在应用首次启动时弹窗向用户申请授权,使用方式请参见 向用户申请授权 。

  3. 创建被调用端UIAbility。 被调用端UIAbility需要实现指定方法的数据接收回调函数、数据的序列化及反序列化方法。在需要接收数据期间,通过on接口注册监听,无需接收数据时通过off接口解除监听。

  4. 配置UIAbility的启动模式。 配置module.json5,将CalleeAbility配置为单实例"singleton"。

Json字段字段说明
“launchType”Ability的启动模式,设置为"singleton"类型。

UIAbility配置标签示例如下:

"abilities":[{"name": ".CalleeAbility","srcEntry": "./ets/CalleeAbility/CalleeAbility.ts","launchType": "singleton","description": "$string:CalleeAbility_desc","icon": "$media:icon","label": "$string:CalleeAbility_label","exported": true
}]
  1. 导入UIAbility模块。
import UIAbility from '@ohos.app.ability.UIAbility';
  1. 定义约定的序列化数据。 调用端及被调用端发送接收的数据格式需协商一致,如下示例约定数据由number和string组成。
import rpc from '@ohos.rpc'
class MyParcelable {num: number = 0;str: string = '';constructor(num: number, string: string) {this.num = num;this.str = string;}mySequenceable(num: number, string: string): void {this.num = num;this.str = string;}marshalling(messageSequence: rpc.MessageSequence): boolean {messageSequence.writeInt(this.num);messageSequence.writeString(this.str);return true;};unmarshalling(messageSequence: rpc.MessageSequence): boolean {this.num = messageSequence.readInt();this.str = messageSequence.readString();return true;};
};
  1. 实现Callee.on监听及Callee.off解除监听。 如下示例在Ability的onCreate注册MSG_SEND_METHOD监听,在onDestroy取消监听,收到序列化数据后作相应处理并返回。应用开发者根据实际业务需要做相应处理。
import type AbilityConstant from '@ohos.app.ability.AbilityConstant';
import UIAbility from '@ohos.app.ability.UIAbility';
import type Want from '@ohos.app.ability.Want';
import hilog from '@ohos.hilog';
import type rpc from '@ohos.rpc';
import type { Caller } from '@ohos.app.ability.UIAbility';const TAG: string = '[CalleeAbility]';
const MSG_SEND_METHOD: string = 'CallSendMsg';
const DOMAIN_NUMBER: number = 0xFF00;class MyParcelable {num: number = 0;str: string = '';constructor(num: number, string: string) {this.num = num;this.str = string;};mySequenceable(num: number, string: string): void {this.num = num;this.str = string;};marshalling(messageSequence: rpc.MessageSequence): boolean {messageSequence.writeInt(this.num);messageSequence.writeString(this.str);return true;};unmarshalling(messageSequence: rpc.MessageSequence): boolean {this.num = messageSequence.readInt();this.str = messageSequence.readString();return true;};
};function sendMsgCallback(data: rpc.MessageSequence): rpc.Parcelable {hilog.info(DOMAIN_NUMBER, TAG, '%{public}s', 'CalleeSortFunc called');// 获取Caller发送的序列化数据let receivedData: MyParcelable = new MyParcelable(0, '');data.readParcelable(receivedData);hilog.info(DOMAIN_NUMBER, TAG, '%{public}s', `receiveData[${receivedData.num}, ${receivedData.str}]`);let num: number = receivedData.num;// 作相应处理// 返回序列化数据result给Callerreturn new MyParcelable(num + 1, `send ${receivedData.str} succeed`) as rpc.Parcelable;
}export default class CalleeAbility extends UIAbility {caller: Caller | undefined;onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {try {this.callee.on(MSG_SEND_METHOD, sendMsgCallback);} catch (error) {hilog.error(DOMAIN_NUMBER, TAG, '%{public}s', `Failed to register. Error is ${error}`)};}onDestroy(): void {try {this.callee.off(MSG_SEND_METHOD);hilog.info(DOMAIN_NUMBER, TAG, '%{public}s', 'Callee OnDestroy');this.releaseCall();} catch (error) {hilog.error(DOMAIN_NUMBER, TAG, '%{public}s', `Failed to register. Error is ${error}`)};}
}
  1. 获取Caller接口,访问被调用端UIAbility。
    1. 导入UIAbility模块。
import UIAbility from '@ohos.app.ability.UIAbility';
2.  获取Caller通信接口。 Ability的context属性实现了startAbilityByCall方法,用于获取指定通用组件的Caller通信接口。如下示例通过this.context获取Ability实例的context属性,使用startAbilityByCall拉起Callee被调用端并获取Caller通信接口,注册Caller的onRelease和onRemoteStateChange监听。应用开发者根据实际业务需要做相应处理。
import { Caller } from '@ohos.app.ability.UIAbility';
import { BusinessError } from '@ohos.base';
import common from '@ohos.app.ability.common';
import deviceManager from '@ohos.distributedDeviceManager';
import hilog from '@ohos.hilog';const TAG: string = '[Page_CollaborateAbility]';
const DOMAIN_NUMBER: number = 0xFF00;
let caller: Caller | undefined;
let dmClass: deviceManager.DeviceManager;function getRemoteDeviceId(): string | undefined {if (typeof dmClass === 'object' && dmClass !== null) {let list = dmClass.getAvailableDeviceListSync();hilog.info(DOMAIN_NUMBER, TAG, JSON.stringify(dmClass), JSON.stringify(list));if (typeof (list) === 'undefined' || typeof (list.length) === 'undefined') {hilog.info(DOMAIN_NUMBER, TAG, 'getRemoteDeviceId err: list is null');return;}if (list.length === 0) {hilog.info(DOMAIN_NUMBER, TAG, `getRemoteDeviceId err: list is empty`);return;}return list[0].networkId;} else {hilog.info(DOMAIN_NUMBER, TAG, 'getRemoteDeviceId err: dmClass is null');return;}
}@Entry
@Component
struct Page_CollaborateAbility {private context = getContext(this) as common.UIAbilityContext;build() {// ...Button('多端协同有返回数据').onClick(() => {this.context.startAbilityByCall({deviceId: getRemoteDeviceId(),bundleName: 'com.samples.stagemodelabilityinteraction',abilityName: 'CalleeAbility'}).then((data) => {if (data !== null) {caller = data;hilog.info(DOMAIN_NUMBER, TAG, 'get remote caller success');// 注册caller的release监听caller.onRelease((msg) => {hilog.info(DOMAIN_NUMBER, TAG, `remote caller onRelease is called ${msg}`);})hilog.info(DOMAIN_NUMBER, TAG, 'remote caller register OnRelease succeed');// 注册caller的协同场景下跨设备组件状态变化监听通知try {caller.onRemoteStateChange((str) => {hilog.info(DOMAIN_NUMBER, TAG, 'Remote state changed ' + str);});} catch (error) {hilog.info(DOMAIN_NUMBER, TAG, `Caller.onRemoteStateChange catch error, error.code: ${JSON.stringify(error.code)}, error.message: ${JSON.stringify(error.message)}`);};}}).catch((error: BusinessError) => {hilog.error(DOMAIN_NUMBER, TAG, `get remote caller failed with ${error}`);});})}
}

getRemoteDeviceId方法参照 通过跨设备启动uiability和serviceextensionability组件实现多端协同无返回数据 。

  1. 向被调用端UIAbility发送约定序列化数据。

    1. 向被调用端发送Parcelable数据有两种方式,一种是不带返回值,一种是获取被调用端返回的数据,method以及序列化数据需要与被调用端协商一致。如下示例调用Call接口,向Callee被调用端发送数据。
import UIAbility, { Caller } from '@ohos.app.ability.UIAbility';
import type rpc from '@ohos.rpc';const MSG_SEND_METHOD: string = 'CallSendMsg';
class MyParcelable {num: number = 0;str: string = '';constructor(num: number, string: string) {this.num = num;this.str = string;};mySequenceable(num: number, string: string): void {this.num = num;this.str = string;};marshalling(messageSequence: rpc.MessageSequence): boolean {messageSequence.writeInt(this.num);messageSequence.writeString(this.str);return true;};unmarshalling(messageSequence: rpc.MessageSequence): boolean {this.num = messageSequence.readInt();this.str = messageSequence.readString();return true;};
};export default class EntryAbility extends UIAbility {// ...caller: Caller | undefined;async onButtonCall(): Promise<void> {try {let msg: MyParcelable = new MyParcelable(1, 'origin_Msg');if (this.caller) {await this.caller.call(MSG_SEND_METHOD, msg);}} catch (error) {hilog.info(DOMAIN_NUMBER, TAG, `caller call failed with ${error}`);};}// ...
}
2.  如下示例调用CallWithResult接口,向Callee被调用端发送待处理的数据originMsg,并将’CallSendMsg’方法处理完毕的数据赋值给backMsg。
import UIAbility, { Caller } from '@ohos.app.ability.UIAbility';
import rpc from '@ohos.rpc';const MSG_SEND_METHOD: string = 'CallSendMsg';
let originMsg: string = '';
let backMsg: string = '';class MyParcelable {num: number = 0;str: string = '';constructor(num: number, string: string) {this.num = num;this.str = string;};mySequenceable(num: number, string: string): void {this.num = num;this.str = string;};marshalling(messageSequence: rpc.MessageSequence): boolean {messageSequence.writeInt(this.num);messageSequence.writeString(this.str);return true;};unmarshalling(messageSequence: rpc.MessageSequence): boolean {this.num = messageSequence.readInt();this.str = messageSequence.readString();return true;};
};export default class EntryAbility extends UIAbility {// ...caller: Caller | undefined;async onButtonCallWithResult(originMsg: string, backMsg: string): Promise<void> {try {let msg: MyParcelable = new MyParcelable(1, originMsg);if (this.caller) {const data = await this.caller.callWithResult(MSG_SEND_METHOD, msg);hilog.info(DOMAIN_NUMBER, TAG, 'caller callWithResult succeed');let result: MyParcelable = new MyParcelable(0, '');data.readParcelable(result);backMsg = result.str;hilog.info(DOMAIN_NUMBER, TAG, `caller result is [${result.num}, ${result.str}]`);}} catch (error) {hilog.info(DOMAIN_NUMBER, TAG, `caller callWithResult failed with ${error}`);};}// ...
}
  1. 释放Caller通信接口。 Caller不再使用后,应用开发者可以通过release接口释放Caller。
import UIAbility, { Caller } from '@ohos.app.ability.UIAbility';export default class EntryAbility extends UIAbility {caller: Caller | undefined;releaseCall(): void {try {if (this.caller) {this.caller.release();this.caller = undefined;}hilog.info(DOMAIN_NUMBER, TAG, 'caller release succeed');} catch (error) {hilog.info(DOMAIN_NUMBER, TAG, `caller release failed with ${error}`);};}
}

为了能让大家更好的学习鸿蒙(HarmonyOS NEXT)开发技术,这边特意整理了《鸿蒙开发学习手册》(共计890页),希望对大家有所帮助:https://qr21.cn/FV7h05

《鸿蒙开发学习手册》:

如何快速入门:https://qr21.cn/FV7h05

  1. 基本概念
  2. 构建第一个ArkTS应用
  3. ……

开发基础知识:https://qr21.cn/FV7h05

  1. 应用基础知识
  2. 配置文件
  3. 应用数据管理
  4. 应用安全管理
  5. 应用隐私保护
  6. 三方应用调用管控机制
  7. 资源分类与访问
  8. 学习ArkTS语言
  9. ……

基于ArkTS 开发:https://qr21.cn/FV7h05

  1. Ability开发
  2. UI开发
  3. 公共事件与通知
  4. 窗口管理
  5. 媒体
  6. 安全
  7. 网络与链接
  8. 电话服务
  9. 数据管理
  10. 后台任务(Background Task)管理
  11. 设备管理
  12. 设备使用信息统计
  13. DFX
  14. 国际化开发
  15. 折叠屏系列
  16. ……

鸿蒙开发面试真题(含参考答案):https://qr18.cn/F781PH

鸿蒙开发面试大盘集篇(共计319页):https://qr18.cn/F781PH

1.项目开发必备面试题
2.性能优化方向
3.架构方向
4.鸿蒙开发系统底层方向
5.鸿蒙音视频开发方向
6.鸿蒙车载开发方向
7.鸿蒙南向开发方向

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

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

相关文章

数据分析之Power BI

POWER QUERY 获取清洗 POWER PIVOT建模分析 如何加载power pivot 文件-选项-加载项-com加载项-转到 POWER VIEW 可视呈现 如何加载power view 文件-选项-自定义功能区-不在功能区中的命令-新建组-power view-添加-确定 POWER MAP可视地图

知识图谱与大数据:区别、联系与应用

目录 前言1 知识图谱1.1 定义1.2 特点1.3 应用 2 大数据2.1 定义2.2 应用 3. 区别与联系3.1 区别3.2 联系 结语 前言 在当今信息爆炸的时代&#xff0c;数据成为了我们生活和工作中不可或缺的资源。知识图谱和大数据是两个关键概念&#xff0c;它们在人工智能、数据科学和信息…

保护电路设计 —(2)过温保护

保护电路设计 —&#xff08;2&#xff09;过温保护 上一讲讲到自锁电路设计&#xff0c;但有的同学还不太清楚怎么去复位这个电路&#xff0c;在这里给出一个例子&#xff0c;去复位这个电路。复位电路也非常简单&#xff0c;使用以下电路即可。如图1所示。 图1:复位电路 为…

HarmonyOS 应用开发之UIAbility组件间交互(设备内)

UIAbility是系统调度的最小单元。在设备内的功能模块之间跳转时&#xff0c;会涉及到启动特定的UIAbility&#xff0c;该UIAbility可以是应用内的其他UIAbility&#xff0c;也可以是其他应用的UIAbility&#xff08;例如启动三方支付UIAbility&#xff09;。 本文将从如下场景…

深入理解指针(7)函数指针变量及函数数组(文章最后放置本文所有原码)

一、函数指针变量 什么是函数指针变量呢&#xff1f; 既然是指针变量&#xff0c;那么它指向的一定是地址&#xff0c;而且我们可以通过地址来调用函数的。 函数是否有地址呢&#xff1f;地址是什么&#xff1f; 经过上面的测试可以看到函数也是有地址的&#xff0c;而且其地…

每日一练 两数相加问题(leetcode)

原题如下&#xff1a; 这道题目是一道链表题&#xff0c;我们对于这种链表类&#xff0c;很显然我们最后输出的是初始节点&#xff0c;所以我们要保留我们的初始头指针&#xff0c;那么我们的第一步一定是把头指针保留一份&#xff0c;然后再让头指针往后进行操作。那么我们进行…

基于Axios封装请求---防止接口重复请求解决方案

一、引言 前端接口防止重复请求的实现方案主要基于以下几个原因&#xff1a; 用户体验&#xff1a;重复发送请求可能导致页面长时间无响应或加载缓慢&#xff0c;从而影响用户的体验。特别是在网络不稳定或请求处理时间较长的情况下&#xff0c;这个问题尤为突出。 服务器压力…

android安卓餐厅点餐课设

一、引言 随着移动互联网的快速发展&#xff0c;手机应用已经成为我们日常生活中不可或缺的一部分。餐饮行业也积极借助移动应用的力量&#xff0c;提供更便捷、高效的点餐服务。本文将介绍一个基于安卓系统开发的餐厅点餐APP的课程设计项目&#xff0c;探讨其设计理念、功能特…

【容器源码篇】Map容器(HashTable,HashMap,TreeMap的特点)

文章目录 ⭐容器继承关系&#x1f339;Map容器&#x1f5d2;️HashTable源码解析构造方法put方法remove方法rehash扩容 &#x1f5d2;️HashMap源码解析构造函数get方法put方法详解 扩容方法详解 &#x1f5d2;️TreeMap源码解析 ⭐容器继承关系 &#x1f339;Map容器 键值对映…

如何在 Mac 上打开、编辑、复制、移动或删除存储在 Windows NTFS 格式 USB 驱动器上的文件 Tuxera NTFS for Mac使用教程

当您获得一台新 Mac 时&#xff0c;它只能读取 Windows NTFS 格式的 USB 驱动器。要将文件添加、保存或写入您的 Mac&#xff0c;您需要一个附加的 NTFS 驱动程序。Tuxera 他可以帮忙实现这一功能&#xff01; Tuxera可以轻松转换驱动器&#xff1a;无论使用Windows PC还是Mac&…

OpenGL的MVP矩阵理解

OpenGL的MVP矩阵理解 右手坐标系 右手坐标系与左手坐标系都是三维笛卡尔坐标系&#xff0c;他们唯一的不同在于z轴的方向&#xff0c;如下图&#xff0c;左边是左手坐标系&#xff0c;右边是右手坐标系 OpenGL中一般用的是右手坐标系 1.模型坐标系&#xff08;Local Space&…

42 ajax 下载文件未配置 responseType blob 导致的文件异常

前言 这是一个最近的关于文件下载碰到的一个问题 主要的情况是, 基于 xhr 发送请求, 获取下载的文件 然后 之后 xhr 这边拿到 字节序列之后, 封装 blob 来进行下载 然后 最开始我们这边没有配置 responseType 为 blob, arraybuffer, 然后 导致下载出来的 文件大小超过了…

Image-Adaptive YOLO for Object Detection in Adverse Weather Conditions(IA-YOLO)

1、总体概述 基于深度学习的目标检测在常规条件的数据集可以获得不错的结果&#xff0c;但是在环境、场景、天气、照度、雾霾等自然条件的综合干扰下&#xff0c;深度学习模型的适应程度变低&#xff0c;检测结果也随之下降&#xff0c;因此研究在复杂气象条件下的目标检测方法…

警务数据仓库的实现

目录 一、SQL Server 2008 R2&#xff08;一&#xff09;SQL Server 的服务功能&#xff08;二&#xff09;SQL Server Management Studio&#xff08;三&#xff09;Microsoft Visual Studio 二、创建集成服务项目三、配置“旅馆_ETL”数据流任务四、配置“人员_ETL”数据流任…

k8s安装traefik作为ingress

一、先来介绍下Ingress Ingress 这个东西是 1.2 后才出现的&#xff0c;通过 Ingress 用户可以实现使用 nginx 等开源的反向代理负载均衡器实现对外暴露服务&#xff0c;以下详细说一下 Ingress&#xff0c;毕竟 traefik 用的就是 Ingress 使用 Ingress 时一般会有三个组件: …

基于SSM的高校普法系统(有报告)。Javaee项目。ssm项目。

演示视频&#xff1a; 基于SSM的高校普法系统&#xff08;有报告&#xff09;。Javaee项目。ssm项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&#xff0c;通过Spring Spri…

inkscape中文版本 G代码生成器(支持中英文及数字)使用

inkscape G代码生成器&#xff08;支持中英文及数字&#xff09;使用 1 inkscape安装1. 界面介绍2. 基本操作3. 图形编辑4. 图层管理5. 文件操作6. 高级功能7. 学习资源 2 laserengraver插件安装3 inkscape 使用candle 验证G代码效果 1 inkscape安装 跟着提示默认按键即可。 软…

HTML网站的概念

目录 前言&#xff1a; 1.什么是网页&#xff1a; 2.什么是网站&#xff1a; 示例&#xff1a; 3.服务器&#xff1a; 总结&#xff1a; 前言&#xff1a; HTML也称Hyper Text Markup Language&#xff0c;意思是超文本标记语言&#xff0c;同时HTML也是前端的基础&…

Linux 环境安装Nginx—源码和Dokcer两种安装方式

一、源代码编译安装Nginx 1.下载最新nginx源码 以nginx-1.25.3.tar.gz为例&#xff1a; 可以使用命令(联网)&#xff1a;curl -O http://nginx.org/download/nginx-1.25.3.tar.gz或在官网下载.tar.gz 2.解压缩 tar -zxvf nginx-1.25.3.tar.gz cd nginx-1.25.3/ 3.安装依赖…

HarmonyOS实战开发-实现自定义弹窗

介绍 本篇Codelab基于ArkTS的声明式开发范式实现了三种不同的弹窗&#xff0c;第一种直接使用公共组件&#xff0c;后两种使用CustomDialogController实现自定义弹窗&#xff0c;效果如图所示 相关概念 AlertDialog&#xff1a;警告弹窗&#xff0c;可设置文本内容和响应回调…