HarmonyOS - 实现多设备协同开发实战教程~

news2025/1/13 3:35:51

前言

现在随着个人设备越来越多,越来越需要多个设备之间相互感知和连接,设备和设备之间可以相互联动,形成互联互通的场景,而搭载HarmonyOS的设备恰好可以满足这一点 。下面通过开发一个HarmonyOS的多端分布式表白应用来实现设备之间的相互联动。

项目介绍

H5页面可以实现一些比较特殊的页面效果,所以选择在应用中集成H5页面。应用可以将页面直接投放到附近其他HarmonyOS设备上,实现多端设备分布式显示,同时应用可以跨端控制,更新应用页面,形成多设备协同的效果。

下面是效果展示:

多设备协同原理

HarmonyOS 给应用开发者提供了一套在多个设备不同应用之间进行任务流转的API接口,实现设备协同需要关注 流转任务管理服务分布式任务调度

流转任务管理服务:在流转发起端,接受用户应用程序注册,提供流转入口、状态显示、退出流转等管理能力。

分布式任务调度:提供远程服务启动、远程服务连接、远程迁移等能力,并通过不同能力组合,支撑用户应用程序完成跨端迁移或多端协同的业务体验。

分布式安全:提供E2E的加密通道,为用户应用程序提供安全的跨端传输机制,保证“正确的人,通过正确的设备,正确地使用数据”。

分布式软总线:使用基于手机、平板、智能穿戴、智慧屏等分布式设备的统一通信基座,为设备之间的互联互通提供统一的分布式通信能力。

任务流转流程:设备A和设备B登录相同的华为账号,在同一网络下。设备A向设备B发起协同,应用程序需先向系统的流转任务管理服务 注册回调,获取到设备B的 DeviceId 等设备信息,设备A初始化分布式任务调度,通过设备DeviceId指定设备发起协同,设备B接收到协同请求,初始化分布式任务调度,启动对应的应用程序,把流转的状态上报给流转任务管理服务,流转任务管理服务返回流转结果,完成一次设备A到设备B的任务流转。

理解分布式软总线

分布式:指的是一种运行方式,简单来说就是任务可以在一个设备上运行,也可以多个设备连接起来一起运行,在多个设备中没有一个绝对的中心。

总线:简单了解一下“总线”的概念,在计算机系统中,各个部件之间传送信息的通道叫总线,外部设备通过相应的接口与总线相连接,组成了整个计算机系统。

分布式软总线:所以不难理解分布式软总线其实就是实现多个设备之间的连接,传递消息,实现任务多端运行的一种技术。在HarmonyOS中,底层已经帮我们实现了设备之间的组网、发现和连接,所以并不需要关心设备怎么通信,只需要调用底层封装好的接口,实现多设备协同就可以了。

实现步骤

实现分布式多设备协同,需要实现跨端启动应用、后台PA服务、分布式数据同步的功能,具体实现流程如下

一、跨设备启动应用

多设备协同实现的前提,需要在多端安装相同的应用,而在现实使用环境中,在多个设备中安装一个相同的应用还是一个比较麻烦的事。而HarmonyOS的原子化服务则不需要用户手动安装,由系统程序框架后台安装后即可使用,在HarmonyOS的服务中心以服务卡片的形式展示。

应用由原子化服务平台(Huawei Ability Gallery)管理和分发,只需要上传到原子化服务平台(Huawei Ability Gallery)即可,在多设备协同中,当设备A的应用向设备B的应用发起多端协同,如果设备B上没有安装对应服务,HarmonyOS会自动下载相关原子化服务,和A端的应用一起进行多端协同。

跨设备启动应用,也就是设备A上的应用可以拉起设备B上的应用。因为原子化服务应用免安装的特性,所以不用关心应用在多设备上的安装,只需实现跨设备启动应用即可。

1. 创建原子化服务

以原子化服务的形式创建项目, 原子化服务的特点是支持免安装,没有应用图标,只在 HarmanoyOS 服务中心以卡片的形式展现,支持跨端迁移和多端协同。
在新建项目时选择Atomic Service,创建一个原子化服务,同时打开Show in service center 开关,自动创建服务卡片,去掉TV的勾选状态,目前服务卡片不支持TV设备。

2. 创建HarmonyOS IDL接口

选择项目的module目录,点击鼠标右键,选择New>Idl File,如下图:

IDL是HarmonyOS的接口描述语言,可以实现IPC跨进程间通信,接口的提供方是服务端,客户端绑定应用的服务来进行交互。

在IDL中定义服务端接口,代码如下。

登录后复制

**interface** com.wealchen.multipoint.IMultiPointIdl {

  *//启动服务*
  void serviceStart([in] int code);

  *//发送消息*
  void sendMsg([in] int code,[in]int extras);

  *//停止服务*
  int serviceStop();
}

创建的IDL接口通过编译会在build > generated > source > Idl> 目录 debugrelease 下自动生成对应的接口类、桩类和代理类,如下图。

3. 实现后台接口服务

在HarmonyOS中,客户端绑定服务端后,获取到序列化的IRemoteObject对象,通过IRemoteObject对象实现客户端与服务端的通信,IRemoteObject在编译生成的桩类和代理类中已经完成了对象的创建和消息发送的实现,具体可查看上图中自动生成的代码。

创建一个Service后台服务MultiPointService,提供给客户端连接,并实现IDL中定义的接口,在接口实现中启动FA页面,接收客户端发送的消息,具体代码如下:

登录后复制

**public** **class** MultiPointService **extends** Ability {
  *// DESCRIPTOR 保持与 MultiPointIdlStub 和 MultiPointIdlProxy中的一致*
  **private** static final String DESCRIPTOR = "com.wealchen.multipoint.IMultiPointIdl";
  **private** static final String TAG = MultiPointService.class.getName();

  @Override
  **protected** void onStart(Intent intent) {
    **super**.onStart(intent);
  }

  @Override
  **protected** IRemoteObject onConnect(Intent intent) {
    **return** **new** MultiPointRemoteObject(DESCRIPTOR);
  }


  **private** **class** MultiPointRemoteObject **extends** MultiPointIdlStub {
    **public** MultiPointRemoteObject(String descriptor) {
      **super**(descriptor);
    }

    @Override
    **public** void serviceStart(int _code) **throws** RemoteException {
      *//启动WebViewAbility页面*
      Intent intent = **new** Intent();
      Operation operation = **new** Intent.OperationBuilder().withBundleName(getBundleName())
          .withAbilityName(WebViewAbility.class.getName()).build();
      intent.setOperation(operation);
      intent.setParam(Constants.INTENT_STAR_PARAM, _code);
      startAbility(intent);
      LogUtil.debug(TAG, "serviceStart : start WebViewAbility " + _code);
    }

    @Override
    **public** void sendMsg(int _code, int _extras) **throws** RemoteException {
      LogUtil.debug(TAG, "sendMsg  code: " + _code + " extras: " + _extras);
    }

    @Override
    **public** int serviceStop() **throws** RemoteException {
      **return** 0;
    }
  }
}
4. 实现跨端启动应用

启动跨端应用需指定设备的DeviceId,通过设备管理器DeviceManager,可获取到当前同一网络下所有不是待机状态的设备,拿到DeviceId,DeviceName等设备信息,具体代码如下:

登录后复制

 *//获取同一网络下的设备*
    List<DeviceInfo> deviceInfos = DeviceManager.getDeviceList(DeviceInfo.FLAG_GET_ONLINE_DEVICE);
    **if** (deviceInfos == **null** && deviceInfos.size() == 0) {
      **return**;
    }
    **for** (int i = 0; i < deviceInfos.size(); i++) {
      String devId = deviceInfos.get(i).getDeviceId();
      String devName = deviceInfos.get(i).getDeviceName();
    }

通过DeviceId可以与指定设备的后台服务MultiPointService连接,实现长期交互,系统提供了connectAbility方法,实现跨设备PA连接与断开连接的能力,通过AbilitySlice的connectAbility接口跨设备连接到后台服务MultiPointService,发送消息到指定设备,设备在接收到消息之后可以执行相应的任务,从而实现跨设备应用任务的调度,连接服务实现代码如下:

登录后复制

 *//启动远端设备的FA*
  **private** void startAbilityFa(String devicesId, String event, int localExtras) {
    Intent intent = **new** Intent();
    Operation operation =
        **new** Intent.OperationBuilder()
            .withDeviceId(devicesId)
            .withBundleName(getBundleName())
            .withAbilityName(MultiPointService.class.getName())
            .withFlags(Intent.FLAG_ABILITYSLICE_MULTI_DEVICE)
            .build();
    intent.setOperation(operation);
    boolean connectFlag = connectAbility(intent, **new** IAbilityConnection() {
      @Override
      **public** void onAbilityConnectDone(ElementName elementName, IRemoteObject remoteObject, int extra) {
        LogUtil.debug(TAG, "onAbilityConnectDone extra:" + extra);
        multiPointIdl = MultiPointIdlStub.asInterface(remoteObject);
        **try** {
          **if** (multiPointIdl != **null**) {
            **switch** (event) {
              **case** "serviceStart":
                multiPointIdl.serviceStart(0);
                **break**;
              **case** "sendMsg":
                multiPointIdl.sendMsg(1001, localExtras);
                **break**;
            }
          }
        } **catch** (RemoteException e) {
          LogUtil.error(TAG, "connect successful,but have remote exception");
        }
      }

      @Override
      **public** void onAbilityDisconnectDone(ElementName elementName, int extra) {
        LogUtil.debug(TAG, "extra " + extra + " elementName " + elementName.getAbilityName());
        disconnectAbility(**this**);
      }
    });
    **if** (connectFlag) {
      Toast.toast(**this**, "transmit successful!", TOAST_DURATION);
    } **else** {
      Toast.toast(**this**, "transmit failed!Please try again later.", TOAST_DURATION);
    }
  }

二、多端设备协同

多设备协同可以实现对跨端设备的控制,使用HarmonyOS的分布式数据服务,不同设备之间的数据可以实时更新并显示在界面上。

1. 分布式数据服务介绍

分布式数据服务是HarmonyOS为应用程序提供不同设备间同步数据的能力,通过使用分布式数据接口,应用程序将数据保存到分布式数据库中,不同设备之间可以通过分布式数据服务互相访问,支持数据在相同帐号的多端设备之间相互同步。

HarmonyOS的分布式数据库是一种NoSQL类型数据库,其数据以键值对key-value的形式进行组织、索引和存储,分布式数据服务包含五部分:

服务接口:提供专门的数据库创建、数据访问、数据订阅等接口给应用程序调用,接口支持KV数据模型,支持常用的数据类型,同时确保接口的兼容性、易用性和可发布性。

服务组件:负责服务内元数据管理、权限管理、加密管理、备份和恢复管理以及多用户管理等、同时负责初始化底层分布式DB的存储组件、同步组件和通信适配层。

存储组件:负责数据的访问、数据的缩减、事务、快照、数据库加密,以及数据合并和冲突解决等特性。

同步组件:连结了存储组件与通信组件,其目标是保持在线设备间的数据库数据一致性,包括将本地产生的未同步数据同步给其他设备,接收来自其他设备发送过来的数据,并合并到本地设备中。

通信适配层:负责调用底层公共通信层的接口完成通信管道的创建、连接,接收设备上下线消息,维护已连接和断开设备列表的元数据,同时将设备上下线信息发送给上层同步组件,同步组件维护连接的设备列表,同步数据时根据该列表,调用通信适配层的接口将数据封装并发送给连接的设备。

2. 创建分布式数据库

创建分布式数据库管理器,设置是否开启加密,自动同步功能,定义数据存储和查询方法,使用手动同步数据的方法实现数据的同步,详细代码代码如下:

登录后复制

**public** **class** MyKvStoreManager {
  **private** static final String TAG = MyKvStoreManager.class.getName();
  **private** static MyKvStoreManager instance = **null**;
  **private** static SingleKvStore singleKvStore = **null**;
  **private** static KvManager kvManager;

  **private** MyKvStoreManager(Context context) {
    createKVManager(context);
  }
  */**
  ** 创建分布式数据管理器*
  ** \*/*
  **public** static void createKVManager(Context context) {
    KvManagerConfig config = **new** KvManagerConfig(context);
    kvManager = KvManagerFactory.getInstance().createKvManager(config);
    **try** {
      Options options = **new** Options();
      options.setCreateIfMissing(**true**)
          .setAutoSync(**false**)
          .setEncrypt(**false**)
          .setKvStoreType(KvStoreType.SINGLE_VERSION);
      String storeId = "remoteData";
      singleKvStore = kvManager.getKvStore(options, storeId);
    } **catch** (KvStoreException e) {
      LogUtil.error(TAG, "getKvStore:" + e.getKvStoreErrorCode());
    }
  }

  */**
  ** 存储数据*
  ** \*/*
  **public** static void putKvStore(String key, String value) {
    **if** (singleKvStore == **null**) {
      **return**;
    }
    **try** {
      singleKvStore.putString(key, value);
    } **catch** (KvStoreException e) {
      LogUtil.debug(TAG, "putString:" + e.getKvStoreErrorCode());
    }
  }

  */**
  ** 查询数据*
  ** \*/*
  **public** static String getKvStore(String key) {
    String valueString = "";
    **try** {
      valueString = singleKvStore.getString(key);
    } **catch** (KvStoreException e) {
      LogUtil.debug(TAG, "getString:" + e.getKvStoreErrorCode());
    }
    **return** valueString;
  }

  */**
  ** 手动同步数据*
  ** \*/*
  **public** static void syncData() {
    List<DeviceInfo> deviceInfoList = kvManager.getConnectedDevicesInfo(DeviceFilterStrategy.NO_FILTER);
    List<String> deviceIdList = **new** ArrayList<>();
    **for** (DeviceInfo deviceInfo : deviceInfoList) {
      deviceIdList.add(deviceInfo.getId());
      LogUtil.debug(TAG,"syncData getId: "+deviceInfo.getId());
    }
    **if** (deviceIdList.isEmpty()){
      **return**;
    }
    singleKvStore.sync(deviceIdList, SyncMode.PUSH_PULL);
  }
   */**
  ** 订阅数据同步结果*
  ** \*/*
  **public** static void dataChangeObserver(KvStoreObserver storeObserver) {
    singleKvStore.subscribe(SubscribeType.SUBSCRIBE_TYPE_REMOTE, storeObserver);
  }
}
3. 订阅分布式数据变化

订阅分布式数据变化客户端应用需要实现KvStoreObserver接口,KvStoreObserver接口回调中返回数据更新的结果,根据返回结果应用执行相应的任务,或者更新界面,从而实现多端设备的同步,注意:回调方法中不允许更新UI组件等阻塞动作。

更新UI可以通过getMainTaskDispatcher().asyncDispatch()切换到UI主线程更新UI的,详细代码如下:

登录后复制

MyKvStoreManager.dataChangeObserver(**new** KvStoreObserver() {
  @Override
  **public** void onChange(ChangeNotification changeNotification) {
    LogUtil.debug(TAG, "dataChangeObserver");
    List<Entry> updateEntries = changeNotification.getUpdateEntries();
    **for** (int i = 0; i < updateEntries.size(); i++) {
      String key = updateEntries.get(i).getKey();
      String value = updateEntries.get(i).getValue().getString();
      LogUtil.debug(TAG, "dataChangeObserver key:" + key + " value:" + value);
      getMainTaskDispatcher().asyncDispatch(**new** Runnable() {
        @Override
        **public** void run() {
          **switch** (key) {
            **case** JS_ADD_NAME:
              *//添加名字*
              setJsAddName(value);
              **break**;
            **case** JS_DEL_NAME:
              *//删除名字*
              setJsDelName(value);
              **break**;
            **default**:
              updateListView(key, value);
              **break**;
          }
        }
      });

    }
  }
});

三、WebView与JavaScript的交互

1. 在WebView调用H5页面的JavaScript方法

更新H5页面时,WebView需要调用JavaScript,通过WebView.executeJs()传入H5页面中对应的方法名称,实现应用调用页面内的JavaScript方法,实现的代码如下:

登录后复制

 **private** void setJsAddName(String addName) {
    LogUtil.debug(TAG, "addName:" + addName);
    String add_js = String.format("javascript:addName('%s')", addName);
    webView.executeJs(add_js, **new** AsyncCallback<String>() {
      @Override
      **public** void onReceive(String msg) {
        *// 在此确认返回结果*
        LogUtil.debug(TAG, "executeJs onReceive:" + msg);
      }
    });
  }

H5页面定义的方法:

**function** addName(name){
    **var** tpl = document.getElementById('name').innerText
    **var** str = name +'\n'
    document.getElementById('name').innerText = tpl.concat(str)
    **return** name
}
2. H5页面调用应用中的方法

WebView组件通过注入回调对象到页面内容,实现在H5页面中调用应用中的方法。

H5页面调用应用中的方法:

登录后复制

**function** callToApp() {
  **if** (window.showDeviceList && window.showDeviceList.call) {
    **var** result = showDeviceList.call("showDeviceList");
  }
}

应用中定义的方法:

webView.addJsCallback("showDeviceList", **new** JsCallback() {
  @Override
  **public** String onCallback(String s) {
    getMainTaskDispatcher().asyncDispatch(**new** Runnable() {
      @Override
      **public** void run() {
        **switch** (s) {
          **case** "showDeviceList":
            **if** (!bottomDialog.isShowing()) {
              bottomDialog.show();
            }
            **break**;
        }
      }
    });
    **return** method;
  }
});

总结

应用实现了包括原子化服务、任务流转、跨端启动应用、分布式数据服务、多端设备协同、WebView组件与JavaScript交互的功能,其中包含了HarmonyOS系统才具有的功能。以上只是简单介绍了HarmonyOS特有功能的实现,当然HarmonyOS的特性不止这些,更多的功能和实现还需要开发者去探索。

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

《鸿蒙 (OpenHarmony)开发学习手册》

入门必看:https://qr21.cn/FV7h05

  1. 应用开发导读(ArkTS)
  2. ……

HarmonyOS 概念:https://qr21.cn/FV7h05

  1. 系统定义
  2. 技术架构
  3. 技术特性
  4. 系统安全

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

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

开发基础知识: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.……

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

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

相关文章

STM32_ESP8266 连接阿里云 操作图解

一、烧录MQTT固件 ESP8266出厂时&#xff0c;默认是&#xff1a;AT固件。连接阿里云需要&#xff1a;MQTT固件。 因此&#xff0c;我们需要给8266重新烧录 MQTT固件。 针对“魔女开发板&#xff0c;ESP8266模块烧录MQTT固件&#xff0c;图解教程如下&#xff1a; ESP8266 烧录 …

代码随想录算法训练营|二叉树总结

二叉树的定义&#xff1a; struct TreeNode {int val;TreeNode* left;TreeNode* right;TreeNode():val(0),left(nullptr),right(nullptr){}TreeNode(int val):val(val),left(nullptr),right(nullptr){}TreeNode(int val,TreeNode* left,TreeNode* right):val(val),left(left),…

网工内推 | 证券公司急招网工,base上海,年薪50W

01 广发证券 招聘岗位&#xff1a;网络工程师 任职要求&#xff1a; 1、懂基础建设网络&#xff0c;虚拟化&#xff0c;或者服务器中任意一个都可以&#xff08;需资深&#xff09;2、985/211本硕 3、年龄最好35以下 薪资待遇&#xff1a; 1、工作时间9:00-17:00 2、根据资历…

思腾合力邀您共赴第二届世界元宇宙大会

由中国仿真学会、中国指挥与控制学会和北京理工大学共同主办&#xff0c;上海市嘉定区安亭镇人民政府和中国仿真学会元宇宙专业委员会承办的第二届世界元宇宙大会大会以“虚实相生、产业赋能”为主题&#xff0c;聚焦元宇宙关键技术发展的共性问题&#xff0c;交流元宇宙技术产…

Window系统GPT-SoVITS配置安装

GPT-SoVITS配置安装 GPT-SoVITS配置Python下载以及安装源文件安装依赖 运行整理在安装配置环境时遇到的报错总结 GPT-SoVITS配置 作者链接 Python下载以及安装 版本这里根据教程的版本走即可&#xff0c;这里不会安装python或者不会配置环境的参考我之前的文章 Python 3.9,…

C# GTS四轴运动控制器实例(固高科技步进电机不带编码器)

注&#xff1a;由于电机不带编码器&#xff0c;无法做home和当前位置信息读取&#xff01; 功能&#xff1a; 三个轴的点位运动&#xff1a;前进后退&#xff0c;并分别显示每个轴的移动脉冲数(可以换算为距离)&#xff01; 开发环境&#xff1a;VS2017 硬件设备&#xff1a;固…

编程笔记 Golang基础 007 第一个程序:hello world 使用Goland

编程笔记 Golang基础 007 第一个程序&#xff1a;hello world 使用Goland 步骤1&#xff1a;启动GoLand并创建新项目步骤2&#xff1a;创建主包和主函数步骤3&#xff1a;运行程序小结 开始在Goland环境中编程go语言代码啦。 步骤1&#xff1a;启动GoLand并创建新项目 打开GoL…

[word] word中图片衬于文字下方无法显示 #媒体#微信

word中图片衬于文字下方无法显示 1、如图&#xff0c;图片“衬于文字下方”&#xff0c;文字下方的图象看不见 2、光标这位到图片上这段文字中&#xff0c;点击“格式”&#xff0d;“边框和底纹”&#xff0c;切换到“底纹”选项卡。可发现这两段文字底纹被设置成“白色”了 …

【数学建模入门】

数学建模入门 数学建模需要的学科知识怎么学习数学模型如何读好一篇优秀论文数学建模赛题常见类别数学建模常见问题数学建模组队和分工数学建模准备工作 数学建模需要的学科知识 怎么学习数学模型 &#x1f4a6;推荐阅读书籍&#xff1a; 《数学建模算法与应用》&#xff0c;…

OpenGL学习——17.模型

前情提要&#xff1a;本文代码源自Github上的学习文档“LearnOpenGL”&#xff0c;我仅在源码的基础上加上中文注释。本文章不以该学习文档做任何商业盈利活动&#xff0c;一切著作权归原作者所有&#xff0c;本文仅供学习交流&#xff0c;如有侵权&#xff0c;请联系我删除。L…

FlinkCDC详解

1、FlinkCDC是什么 1.1 CDC是什么 CDC是Chanage Data Capture&#xff08;数据变更捕获&#xff09;的简称。其核心原理就是监测并捕获数据库的变动&#xff08;例如增删改&#xff09;&#xff0c;将这些变更按照发生顺序捕获&#xff0c;将捕获到的数据&#xff0c;写入数据…

Vue | (二)Vue组件化编程 | 尚硅谷Vue2.0+Vue3.0全套教程

文章目录 &#x1f4da;模块与组件、模块化与组件化&#x1f4da;非单文件组件&#x1f407;基本使用&#x1f407;关于组件的几个注意点&#x1f407;组件的嵌套 &#x1f4da;单文件组件&#x1f407;一个.vue 文件的组成&#x1f407;实例 学习链接&#xff1a;尚硅谷Vue2.0…

多线程、分布式运行用例

python多线程 threading模块 多线程实例 # -*- coding: utf-8 -*- # Time : 2024/2/7 15:50 # Author : 居里夫人吃橘子 # File : class01.py # Software: PyCharm import threading from time import sleepdef run(name):print(name 该起床了)sleep(2)print(name …

EXCEL使用VBA一键批量转换成PDF

EXCEL使用VBA一键批量转换成PDF 上图是给定转换路径 Sub 按钮1_Click() Dim a(1 To 1000) As String Dim a2 As String Dim myfile As String Dim wb As Workbook a2 Trim(Range("a2"))myfile Dir(a2 & "\" & "*.xls")k 0Do While m…

【LeetCode】树的BFS(层序遍历)精选6题

目录 1. N 叉树的层序遍历&#xff08;中等&#xff09; 2. 二叉树的锯齿形层序遍历&#xff08;中等&#xff09; 3. 二叉树的最大宽度&#xff08;中等&#xff09; 4. 在每个树行中找最大值&#xff08;中等&#xff09; 5. 找树左下角的值&#xff08;中等&#xff09…

2024.2.20

使用多进程完成两个文件的拷贝&#xff0c;父进程拷贝前一半&#xff0c;子进程拷贝后一半&#xff0c;父进程回收子进程的资源 #include<myhead.h> int main(int argc, const char *argv[]) {char str[100]"";puts("please input str:");//从终端读…

手动实现new操作符

<script>//前置知识// 每一个函数在创建之初就会有一个prototype属性&#xff0c;这个属性指向函数的原型对象// function abc(){// }// abc.prototype--> {constructor: f}// 在JS中任意的对象都有内置的属性叫做[[prototype]]这是一个私有属性&#xff0c;这个私有属…

GEE数据集——美国两个主要石油和天然气(OG)产区内与石油和天然气(OG)相关的基础设施的位置

该数据集提供了美国两个主要石油和天然气&#xff08;O&G&#xff09;产区内与石油和天然气&#xff08;O&G&#xff09;相关的基础设施的位置&#xff1a;德克萨斯州西部和新墨西哥州南部二叠纪盆地的特拉华子盆地以及犹他州的乌因塔盆地。前言 – 人工智能教程 石油…

deep learning 代码笔记

1. pandas数据读取和预处理 # import pandas and load dataset import pandas as pd names [Sex, Length, Diameter, Height, Whole_weight, Shucked_weight, Viscera_weight, Shell_weight, Rings] data pd.read_csv(data_file, headerNone, namesnames) print(data) …

【前沿】头戴式光场显示技术研究进展

摘要&#xff1a;光场显示器旨在通过重建三维场景在不同方向发出的几何光线来渲染三维场景的视觉感知&#xff0c;从而为人的视觉系统提供自然舒适的视觉体验&#xff0c;解决传统平面立体三维显示器中的聚散调节冲突问题。近年来&#xff0c;多种光场显示方法被尝试应用到头戴…