2024年鸿蒙最新HarmonyOS 应用开发之多端协同(3),多亏这份《秋招+金九银十-腾讯面试题合集》跳槽薪资翻倍

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!


img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上鸿蒙开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

需要这份系统化的资料的朋友,可以戳这里获取

在设备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组件实现多端协同无返回数据 。

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

SON.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组件实现多端协同无返回数据 。




[外链图片转存中...(img-rVWTCeWN-1715741146197)]
[外链图片转存中...(img-KR828hUp-1715741146197)]

**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化的资料的朋友,可以戳这里获取](https://bbs.csdn.net/topics/618636735)**


**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值