HarmonyOS 应用开发之多端协同

news2024/11/28 5:47:49

多端协同流程

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

图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接口为系统API
  try {
    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的AbilityContext
        this.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接口启动的ServiceExtensionAbility
        this.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的AbilityContext
        this.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的AbilityContext
       this.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的UIAbilityContext
        this.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给Caller
  return 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.coloradmin.cn/o/1560583.html

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

相关文章

春招冲刺百题计划--矩阵篇

289. 生命游戏 题目&#xff1a; 给定一个包含 m n 个格子的面板&#xff0c;每一个格子都可以看成是一个细胞。每个细胞都具有一个初始状态&#xff1a; 1 即为 活细胞 &#xff08;live&#xff09;&#xff0c;或 0 即为 死细胞 &#xff08;dead&#xff09;。每个细胞与…

mysql执行脚本导入表和数据后中文注释乱码解决

本人在使用不同版本下进行操作时&#xff0c;就会出现中文乱码的问题。例如我本地安装mysql8&#xff0c;服务器安装的是mysql5&#xff0c;然后本地连接服务器的mysql后&#xff0c;执行SQL脚本之后发现中文全部乱码 使用工具查看&#xff0c;注释也都是乱码 解决方案 本地…

PyLMKit(9):ChatTable与你的表格聊天,表格问答

功能介绍 与你的结构化数据聊天&#xff1a;支持主流数据库、表格型excel等数据&#xff01; ChatDB&#xff1a;支持数据库问答ChatTable&#xff1a;支持txt,excel,csv等pandas dataframe表格的问答 1.下载安装 pip install pylmkit -U pip install pandasql2.ChatTable实…

百度“超模”、三大开发神器组团出道?李彦宏2024年度演讲提前剧透!

未来程序员会消失吗&#xff1f;一个大模型能适配一切吗&#xff1f;大模型太贵用不起怎么办&#xff1f;AI时代最好用的工具长什么样&#xff1f;——这些行业热议问题&#xff0c;将在两周后得到答案。 4月16日&#xff0c;百度创始人、董事长兼首席执行官李彦宏将在2024百度…

构建安全高效的用户登录系统:登录流程设计与Token验证详解

在当今数字化时代&#xff0c;用户登录系统是几乎所有在线服务的基础。然而&#xff0c;随着网络安全威胁的不断增加&#xff0c;设计一个安全可靠的登录系统变得至关重要。本文将深入探讨用户登录流程的设计原则以及Token验证的实现方式&#xff0c;带您了解如何构建安全高效的…

题目:小蓝的零花钱(蓝桥OJ 3236)

问题描述&#xff1a; 解题思路&#xff1a; 计算每个可以切割的位置的费用(cnt)&#xff0c;从小到大枚举每个切割费用&#xff0c;当切割总费用大于手里的钱时停止&#xff0c;最后枚举的已切割位置总数就是答案。 #include<bits/stdc.h> using namespace std; const …

科学高效备考2024年汉字小达人:历年真题详细解析-古诗文专题

距离2024年第11届汉字小达人比赛还有七个多月的时间&#xff0c;如何利用这段时间有条不紊地备考呢&#xff1f;我的建议是两手准备&#xff1a;①把小学1-5年级的语文课本上的知识点熟悉&#xff0c;重点是字、词、成语、古诗。阅读理解不需要。②把历年真题刷刷熟&#xff0c…

物料主数据管理难在哪?基于AI算法的数据清洗工具了解一下

引言 数据清洗在处理大量的、不一致的、错误的或者不完整的数据时扮演着至关重要的角色。随着数据量的不断膨胀&#xff0c;有效的数据清洗工具和方法变得尤为重要。 数据清洗作为数据管理和治理的重要组成部分&#xff0c;近年来受到了国家层面的高度重视&#xff0c;并出台了…

Python保留字与obspy安装

保留字&#xff1a; 被编程语言内部定义并保留使用的标识符。Python有33个保留字 andelifimportraiseglobalaselseinreturnnonlocalassertexceptistryTrue breakfinallylambdawhileFalseclassfornotwithNonecontinuefromoryielddefifpassdel 最近在安装obspy时经常&am…

[SpringCloud] Feign Client 的创建 (二) (五)

文章目录 1.自动配置FeignAutoConfiguration2.生成 Feign Client2.1 从Feign Client子容器获取组件2.2 Feign Client子容器的创建2.3 构建Feign Client实例 1.自动配置FeignAutoConfiguration spring-cloud-starter-openfeign 包含了 spring-cloud-openfeign-core FeignAutoCo…

解决echarts xAxis设置type:‘value‘后 x轴有负值的时候 Y轴在0点显示

前提&#xff1a;xAxis设置type:‘value’ 数据&#xff1a;data里面含有负数值&#xff0c;导致Y坐标轴一直在 X&#xff08;0&#xff09;上面显示 解决方案&#xff1a; yAxis里面设置 axisLine: { onZero:false } yAxis:{type: value,name:测试,axisLine: { onZero:false …

Rust 机器学习图形库 petgraph

一、介绍 Petgraph 是一个开源的图数据结构库&#xff0c;提供了非常丰富的图形类型和算法&#xff0c;并且支持将图形以 Graphviz 格式输出&#xff0c;还允许你为图的节点和边赋予任意类型的数据&#xff0c;从而能够灵活地处理和表示复杂的数据关系。 Petgraph 支持边的方…

【Linux多线程】生产者消费者模型

【Linux多线程】生产者消费者模型 目录 【Linux多线程】生产者消费者模型生产者消费者模型为何要使用生产者消费者模型生产者消费者的三种关系生产者消费者模型优点基于BlockingQueue的生产者消费者模型C queue模拟阻塞队列的生产消费模型 伪唤醒情况&#xff08;多生产多消费的…

MySQL数据库----------探索高级SQL查询语句 (二)

目录 一、子查询 1.1多表查询 1.2多层嵌套 1.3 insert语句子查询 1.4update语句子查询 1.5delete语句子查询 1.6EXISTS 1.7子查询&#xff0c;别名as 二、mysql视图 2.1mysql视图介绍 2.2mysql作用场景[图]: 2.3视图功能&#xff1a; 2.4视图和表的区别和联系 区别…

Linux 个人笔记之三剑客 grep sed awk

文章目录 零、预一、grep 文本过滤工具基础篇实战篇 二、sed 字符流编辑器基础篇实战篇 三、awk 文本处理工具基础篇实战篇 四、附xargsuniq & sort基础篇实战篇 cut 零、预 bash 的命令行展开 {} $ echo file_{1..4} file_1 file_2 file_3 file_4$ echo file_{a..d} file_…

【攻防世界】unseping (反序列化与Linux bash shell)

打开题目环境&#xff1a; 1、进行PHP代码审计&#xff0c;通过审计得知需要用到PHP反序列化。找到输出flag的位置为 ping()函数。通过使用 exec() 函数来执行 $ip 并将结果保存在 $result 中&#xff0c;最终输出 $result。 2、接着寻找给 $ip 传参的位置&#xff0c;发现通过…

FastEI论文阅读

前言 研究FastEI有很长时间了&#xff0c;现在来总结一下&#xff0c;梳理一下认知。论文地址&#xff1a;https://www.nature.com/articles/s41467-023-39279-7&#xff0c;Github项目地址&#xff1a;https://github.com/Qiong-Yang/FastEI。 概要 这篇文章做的工作是小分子…

16.面向对象的软件测试技术

主要考点&#xff1a; 1、面向对象相关的基础概念&#xff1b;&#xff08;已经在软件工程的课程中讲过&#xff0c;要熟悉UML图&#xff0c;知道类和类之间的关系&#xff0c;这些知识也可能结合到下午题考察&#xff09; 2、面向对象的软件测试技术&#xff1b;&#xff08;大…

基于单片机汽车超声波防盗系统设计

**单片机设计介绍&#xff0c;基于单片机汽车超声波防盗系统设计 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于单片机汽车超声波防盗系统设计概要主要涉及利用超声波传感器和单片机技术来实现汽车的安全防盗功能。以下是对…

辽宁梵宁教育:点亮大学生设计技能之光

辽宁梵宁教育作为专注于设计教育的线上机构&#xff0c;对大学生设计技能的提升和就业前景产生了深远的影响。在当前数字化时代&#xff0c;设计技能已逐渐成为各行各业不可或缺的重要能力&#xff0c;而梵宁教育正是抓住了这一机遇&#xff0c;致力于培养具备创新思维和实践能…