鸿蒙开发——键值型数据库的基本使用与跨设备同步

1、简 述

❓ 什么是键值型数据库

键值型数据库(KV-Store)是一种非关系型数据库,其数据以“键值”对的形式进行组织、索引和存储,其中“键”作为唯一标识符。

键值型数据库适合很少数据关系和业务关系的业务数据存储。

另外,因键值型数据库在分布式场景中降低了解决数据库版本兼容问题的复杂度,和数据同步过程中冲突解决的复杂度而被广泛使用。相比于关系型数据库,更容易做到跨设备跨版本兼容。

当需要存储的数据没有复杂的关系模型,比如存储商品名称及对应价格、员工工号及今日是否已出勤等,由于数据复杂度低,更容易兼容不同数据库版本和设备类型,因此推荐使用键值型数据库持久化此类数据。

📢 使用键值型数据库时,需要注意以下几点:

    • 设备协同数据库,针对每条记录,Key的长度≤896 Byte,Value的长度<4 MB。

    • 单版本数据库,针对每条记录,Key的长度≤1 KB,Value的长度<4 MB。

    • 每个应用程序最多支持同时打开16个键值型分布式数据库。

    • 键值型数据库事件回调方法中不允许进行阻塞操作,例如修改UI组件。

    • 单个数据库最多支持注册8个订阅数据变化的回调。

    • 键值型数据库不支持应用程序自定义冲突解决策略。

2、键值型数据库的基本使用

键值型数据库的能力主要是由 @ohos.data.distributedKVStore 模块提供,实现不同设备间数据库的分布式协同能力。通过调用分布式键值数据库各个接口,应用程序可将数据保存到分布式键值数据库中,并可对分布式键值数据库中的数据进行增加、删除、修改、查询、同步等操作。

该模块提供以下分布式键值数据库相关的常用功能:

    • KVManager:分布式键值数据库管理实例,用于获取数据库的相关信息。

    • KVStoreResultSet:提供获取数据库结果集的相关方法,包括查询和移动数据读取位置等。

    • Query:使用谓词表示数据库查询,提供创建Query实例、查询数据库中的数据和添加谓词的方法。

    • SingleKVStore:单版本分布式键值数据库,不对数据所属设备进行区分,提供查询数据和同步数据的方法。

    • DeviceKVStore:设备协同数据库,继承自SingleKVStore,以设备维度对数据进行区分,提供查询数据和同步数据的方法。

以下是键值型数据库持久化功能的相关接口,大部分为异步接口。异步接口均有callback和Promise两种返回形式,下面均以callback形式为例:

// 创建一个KVManager对象实例,用于管理数据库对象。createKVManager(config: KVManagerConfig): KVManager// 指定options和storeId,创建并得到指定类型的KVStore数据库。getKVStore<T>(storeId: string, options: Options, callback: AsyncCallback<T>): void// 添加指定类型的键值对到数据库。put(key: string, value: Uint8Array | string | number | boolean, callback: AsyncCallback<void>): void// 获取指定键的值。get(key: string, callback: AsyncCallback<boolean | string | number | Uint8Array>): void// 从数据库中删除指定键值的数据。delete(key: string, callback: AsyncCallback<void>): void// 通过storeId的值关闭指定的分布式键值数据库。closeKVStore(appId: string, storeId: string, callback: AsyncCallback<void>): void// 通过storeId的值删除指定的分布式键值数据库。deleteKVStore(appId: string, storeId: string, callback: AsyncCallback<void>): void

使用示例

👉🏻 step 1:若要使用键值型数据库,首先要获取一个KVManager实例,用于管理数据库对象。示例代码如下所示(代码13 ~ 25行):

// 导入模块import { distributedKVStore } from '@kit.ArkData';import { window } from '@kit.ArkUI';import { UIAbility } from '@kit.AbilityKit';import { BusinessError } from '@kit.BasicServicesKit';let kvManager: distributedKVStore.KVManager | undefined = undefined;export default class EntryAbility extends UIAbility {  onCreate() {    let context = this.context;    const kvManagerConfig: distributedKVStore.KVManagerConfig = {      context: context,      bundleName: 'com.example.datamanagertest'    };    try {      // 创建KVManager实例      kvManager = distributedKVStore.createKVManager(kvManagerConfig);      console.info('Succeeded in creating KVManager.');      // 继续创建获取数据库    } catch (e) {      let error = e as BusinessError;      console.error(`Failed to create KVManager. Code:${error.code},message:${error.message}`);    }  }}if (kvManager !== undefined) {   kvManager = kvManager as distributedKVStore.KVManager;  //进行后续操作  //...}

👉🏻 step 2:基于step 1创建的kvManager,我们可以创建并获取键值数据库。示例代码如下所示:​​​​​​​

let kvStore: distributedKVStore.SingleKVStore | undefined = undefined;try {  const options: distributedKVStore.Options = {    createIfMissing: true,    encrypt: false,    backup: false,    autoSync: false,    // kvStoreType不填时,默认创建多设备协同数据库    kvStoreType: distributedKVStore.KVStoreType.SINGLE_VERSION,    // 多设备协同数据库:kvStoreType: distributedKVStore.KVStoreType.DEVICE_COLLABORATION,    securityLevel: distributedKVStore.SecurityLevel.S1  };  kvManager.getKVStore<distributedKVStore.SingleKVStore>('storeId', options, (err, store: distributedKVStore.SingleKVStore) => {    if (err) {      console.error(`Failed to get KVStore: Code:${err.code},message:${err.message}`);      return;    }    console.info('Succeeded in getting KVStore.');    kvStore = store;    // 请确保获取到键值数据库实例后,再进行相关数据操作  });} catch (e) {  let error = e as BusinessError;  console.error(`An unexpected error occurred. Code:${error.code},message:${error.message}`);}if (kvStore !== undefined) {  kvStore = kvStore as distributedKVStore.SingleKVStore;    //进行后续操作    //...}

👉🏻 基于step 2创建的kvStore对象,我们可以调用put()方法向键值数据库中插入数据。示例代码如下所示:​​​​​​​

const KEY_TEST_STRING_ELEMENT = 'key_test_string';const VALUE_TEST_STRING_ELEMENT = 'value_test_string';try {  kvStore.put(KEY_TEST_STRING_ELEMENT, VALUE_TEST_STRING_ELEMENT, (err) => {    if (err !== undefined) {      console.error(`Failed to put data. Code:${err.code},message:${err.message}`);      return;    }    console.info('Succeeded in putting data.');  });} catch (e) {  let error = e as BusinessError;  console.error(`An unexpected error occurred. Code:${error.code},message:${error.message}`);}

👉🏻 基于step 2创建的kvStore对象,我们调用get()方法获取指定键的值。示例代码如下所示:​​​​​​​

try {  kvStore.put(KEY_TEST_STRING_ELEMENT, VALUE_TEST_STRING_ELEMENT, (err) => {    if (err !== undefined) {      console.error(`Failed to put data. Code:${err.code},message:${err.message}`);      return;    }    console.info('Succeeded in putting data.');    kvStore = kvStore as distributedKVStore.SingleKVStore;    kvStore.get(KEY_TEST_STRING_ELEMENT, (err, data) => {      if (err != undefined) {        console.error(`Failed to get data. Code:${err.code},message:${err.message}`);        return;      }      console.info(`Succeeded in getting data. Data:${data}`);    });  });} catch (e) {  let error = e as BusinessError;  console.error(`Failed to get data. Code:${error.code},message:${error.message}`);}

👉🏻 基于step 2创建的kvStore对象,我们调用delete()方法删除指定键值的数据。示例代码如下所示:​​​​​​​

try {  kvStore.put(KEY_TEST_STRING_ELEMENT, VALUE_TEST_STRING_ELEMENT, (err) => {    if (err !== undefined) {      console.error(`Failed to put data. Code:${err.code},message:${err.message}`);      return;    }    console.info('Succeeded in putting data.');    kvStore = kvStore as distributedKVStore.SingleKVStore;    kvStore.delete(KEY_TEST_STRING_ELEMENT, (err) => {      if (err !== undefined) {        console.error(`Failed to delete data. Code:${err.code},message:${err.message}`);        return;      }      console.info('Succeeded in deleting data.');    });  });} catch (e) {  let error = e as BusinessError;  console.error(`An unexpected error occurred. Code:${error.code},message:${error.message}`);}

👉🏻 基于step 1创建的kvManager对象,我们通过storeId的值关闭指定的分布式键值数据库。示例代码如下所示:​​​​​​​

try {  kvStore = undefined;  kvManager.closeKVStore('appId', 'storeId', (err: BusinessError)=> {    if (err) {      console.error(`Failed to close KVStore.code is ${err.code},message is ${err.message}`);      return;    }    console.info('Succeeded in closing KVStore');  });} catch (e) {  let error = e as BusinessError;  console.error(`An unexpected error occurred. Code:${error.code},message:${error.message}`);}

👉🏻 基于step 1创建的kvManager对象,我们通过storeId的值删除指定的分布式键值数据库。示例代码如下所示:​​​​​​​

try {  kvStore = undefined;  kvManager.deleteKVStore('appId', 'storeId', (err: BusinessError)=> {    if (err) {      console.error(`Failed to close KVStore.code is ${err.code},message is ${err.message}`);      return;    }    console.info('Succeeded in closing KVStore');  });} catch (e) {  let error = e as BusinessError;  console.error(`An unexpected error occurred. Code:${error.code},message:${error.message}`);}

3、键值型数据库跨设备数据同步

数据管理服务提供了两种同步方式:手动同步和自动同步。键值型数据库可选择其中一种方式实现同应用跨设备数据同步。

手动同步

由应用程序调用sync接口来触发,需要指定同步的设备列表和同步模式。同步模式分为:

  • PULL_ONLY(将远端数据拉取到本端);

  • PUSH_ONLY(将本端数据推送到远端);

  • PUSH_PULL(将本端数据推送到远端同时也将远端数据拉取到本端)。

自动同步

在跨设备Call调用实现的多端协同场景中,在应用程序更新数据后,由分布式数据库自动将本端数据推送到远端,同时也将远端数据拉取到本端来完成数据同步,应用不需要主动调用sync接口。

键值型数据库分为两种类型:单版本数据库、多设备协同分布式数据库。两种类型介绍如下:

  • 单版本数据库

单版本是指数据在本地是以单个条目为单位的方式保存,当数据在本地被用户修改时,不管它是否已经被同步出去,均直接在这个条目上进行修改。

多个设备全局只保留一份数据,多个设备的相同记录(主码相同)会按时间最新保留一条记录,数据不分设备,设备之间修改相同的key会覆盖。同步也以此为基础,按照它在本地被写入或更改的顺序将当前最新一次修改逐条同步至远端设备,常用于联系人、天气等应用存储场景。

图片

  • 多设备协同分布式数据库

多设备协同分布式数据库建立在单版本数据库之上,对应用程序存入的键值型数据中的Key前面拼接了本设备的DeviceID标识符,这样能保证每个设备产生的数据严格隔离。数据以设备的维度管理,不存在冲突;支持按照设备的维度查询数据。

底层按照设备的维度管理这些数据,多设备协同数据库支持以设备的维度查询分布式数据,但是不支持修改远端设备同步过来的数据。需要分开查询各设备数据的可以使用设备协同版本数据库。常用于图库缩略图存储场景。

图片

以下是单版本键值型分布式数据库跨设备数据同步功能的相关接口,大部分为异步接口。异步接口均有callback和Promise两种返回形式,下面均以callback形式为例。​​​​​​​

// 创建一个KVManager对象实例,用于管理数据库对象。createKVManager(config: KVManagerConfig): KVManager// 指定options和storeId,创建并得到指定类型的KVStore数据库。getKVStore<T>(storeId: string, options: Options, callback: AsyncCallback<T>): void// 插入和更新数据。put(key: string, value: Uint8Array | string | number | boolean, callback: AsyncCallback<void>): void// 订阅数据库中数据的变化。on(event: 'dataChange', type: SubscribeType, listener: Callback<ChangeNotification>): void// 查询指定Key键的值。get(key: string, callback: AsyncCallback<boolean | string | number | Uint8Array>): void// 在手动模式下,触发数据库同步。sync(deviceIds: string[], mode: SyncMode, delayMs?: number): void

使用分布式键值数据库需要申明权限:ohos.permission.DISTRIBUTED_DATASYNC,同时需要在应用首次启动时弹窗向用户申请授权

👉🏻 step 1:根据配置构造分布式数据库管理实例​​​​​​​

import { distributedKVStore } from '@kit.ArkData';import { window } from '@kit.ArkUI';import { UIAbility } from '@kit.AbilityKit';import { BusinessError } from '@kit.BasicServicesKit';let kvManager: distributedKVStore.KVManager | undefined = undefined;class EntryAbility extends UIAbility {  onWindowStageCreate(windowStage:window.WindowStage) {    let context = this.context;  }} // .... // 获取context之后,构造分布式数据库管理类实例try {  const kvManagerConfig: distributedKVStore.KVManagerConfig = {    bundleName: 'com.harmonyclassroom.datamanagertest',    context: context  }  kvManager = distributedKVStore.createKVManager(kvManagerConfig);  console.info('Succeeded in creating KVManager.');  // todo 继续创建获取数据库} catch (e) {  let error = e as BusinessError;  console.error(`Failed to create KVManager. Code:${error.code},message:${error.message}`);}if (kvManager !== undefined) {  kvManager = kvManager as distributedKVStore.KVManager;  // 进行后续创建数据库等相关操作  // ...}

👉🏻 step 2:获取并得到指定类型的键值型数据库。

    1. 声明需要创建的分布式数据库ID描述(例如示例代码中的'storeId')。

    2. 创建分布式数据库,建议关闭自动同步功能(autoSync:false),方便后续对同步功能进行验证,需要同步时主动调用sync接口

let kvStore: distributedKVStore.SingleKVStore | undefined = undefined;try {  let child1 = new distributedKVStore.FieldNode('id');  child1.type = distributedKVStore.ValueType.INTEGER;  child1.nullable = false;  child1.default = '1';  let child2 = new distributedKVStore.FieldNode('name');  child2.type = distributedKVStore.ValueType.STRING;  child2.nullable = false;  child2.default = 'zhangsan';  let schema = new distributedKVStore.Schema();  schema.root.appendChild(child1);  schema.root.appendChild(child2);  schema.indexes = ['$.id', '$.name'];  // 0表示COMPATIBLE模式,1表示STRICT模式。  schema.mode = 1;  // 支持在检查Value时,跳过skip指定的字节数,且取值范围为[0,4M-2]。  schema.skip = 0;  const options: distributedKVStore.Options = {    createIfMissing: true,    encrypt: false,    backup: false,    autoSync: false,    // kvStoreType不填时,默认创建多设备协同数据库    // 多设备协同数据库:kvStoreType: distributedKVStore.KVStoreType.DEVICE_COLLABORATION,    kvStoreType: distributedKVStore.KVStoreType.SINGLE_VERSION,    // schema 可以不填,在需要使用schema功能时可以构造此参数,例如:使用谓词查询等。    schema: schema,    securityLevel: distributedKVStore.SecurityLevel.S1  };  kvManager.getKVStore<distributedKVStore.SingleKVStore>('storeId', options, (err, store: distributedKVStore.SingleKVStore) => {    if (err) {      console.error(`Failed to get KVStore: Code:${err.code},message:${err.message}`);      return;    }    console.info('Succeeded in getting KVStore.');    kvStore = store;    // 请确保获取到键值数据库实例后,再进行相关数据操作  });} catch (e) {  let error = e as BusinessError;  console.error(`An unexpected error occurred. Code:${error.code},message:${error.message}`);}if (kvStore !== undefined) {  kvStore = kvStore as distributedKVStore.SingleKVStore;    // 进行后续相关数据操作,包括数据的增、删、改、查、订阅数据变化等操作    // ...}

👉🏻 step 3:订阅分布式数据变化

如需关闭订阅分布式数据变化,调用off('dataChange')关闭。​​​​​​​

try {  kvStore.on('dataChange', distributedKVStore.SubscribeType.SUBSCRIBE_TYPE_ALL, (data) => {    console.info(`dataChange callback call data: ${data}`);  });} catch (e) {  let error = e as BusinessError;  console.error(`An unexpected error occurred. code:${error.code},message:${error.message}`);}

👉🏻 step 4:将数据写入分布式数据库。

    1. 构造需要写入分布式数据库的Key(键)和Value(值)。

    2. 将键值数据写入分布式数据库

const KEY_TEST_STRING_ELEMENT = 'key_test_string';// 如果未定义Schema则Value可以传其他符合要求的值。const VALUE_TEST_STRING_ELEMENT = '{"id":0, "name":"lisi"}';try {  kvStore.put(KEY_TEST_STRING_ELEMENT, VALUE_TEST_STRING_ELEMENT, (err) => {    if (err !== undefined) {      console.error(`Failed to put data. Code:${err.code},message:${err.message}`);      return;    }    console.info('Succeeded in putting data.');  });} catch (e) {  let error = e as BusinessError;  console.error(`An unexpected error occurred. Code:${error.code},message:${error.message}`);}

👉🏻 step 5:查询分布式数据库数据。

    1. 构造需要从单版本分布式数据库中查询的Key(键)。

    2. 从单版本分布式数据库中获取数据

try {  kvStore.put(KEY_TEST_STRING_ELEMENT, VALUE_TEST_STRING_ELEMENT, (err) => {    if (err !== undefined) {      console.error(`Failed to put data. Code:${err.code},message:${err.message}`);      return;    }    console.info('Succeeded in putting data.');    kvStore = kvStore as distributedKVStore.SingleKVStore;    kvStore.get(KEY_TEST_STRING_ELEMENT, (err, data) => {      if (err != undefined) {        console.error(`Failed to get data. Code:${err.code},message:${err.message}`);        return;      }      console.info(`Succeeded in getting data. Data:${data}`);    });  });} catch (e) {  let error = e as BusinessError;  console.error(`Failed to get data. Code:${error.code},message:${error.message}`);}

👉🏻 step 6:同步数据到其他设备。

选择同一组网环境下的设备以及同步模式(需用户在应用首次启动的弹窗中确认选择同步模式),进行数据同步。​​​​​​​

import { distributedDeviceManager } from '@kit.DistributedServiceKit'; let devManager: distributedDeviceManager.DeviceManager;try {  // create deviceManager  devManager = distributedDeviceManager.createDeviceManager(context.applicationInfo.name);  // deviceIds由deviceManager调用getAvailableDeviceListSync方法得到  let deviceIds: string[] = [];  if (devManager != null) {    let devices = devManager.getAvailableDeviceListSync();    for (let i = 0; i < devices.length; i++) {      deviceIds[i] = devices[i].networkId as string;    }  }  try {    // 1000表示最大延迟时间为1000ms    kvStore.sync(deviceIds, distributedKVStore.SyncMode.PUSH_ONLY, 1000);  } catch (e) {    let error = e as BusinessError;    console.error(`An unexpected error occurred. Code:${error.code},message:${error.message}`);  }} catch (err) {  let error = err as BusinessError;  console.error("createDeviceManager errCode:" + error.code + ",errMessage:" + error.message);}
在手动同步的方式下,其中的deviceIds通过调用devManager.getAvailableDeviceListSync方法得到。

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

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

相关文章

零基础认识:交换机,子网掩码,默认网关,以及路由器,IP地址,MAC地址

图解 物理层&#xff1a;使用MAC解决设备的身份证问题 通信的原始时代 很久很久之前&#xff0c;你不与任何其他电脑相连接&#xff0c;孤苦伶仃。 直到有一天&#xff0c;你希望与另一台电脑 B 建立通信&#xff0c;于是你们各开了一个网口&#xff0c;用一根网线连接了起来…

【Canvas与标牌】Water Outages(停水)标牌

【成图】 【代码】 <!DOCTYPE html> <html lang"utf-8"> <meta http-equiv"Content-Type" content"text/html; charsetutf-8"/> <head><title>Wate Outages停水标志 Draft1</title><style type"tex…

CentOS 7 上安装 MySQL 8.0.40 (二进制安装)

要在 CentOS 7 上安装 MySQL 8.0.40&#xff0c;按照以下步骤操作&#xff1a; 下载安装包。 https://dev.mysql.com/downloads/mysql/ 下载之前查看系统c版本 解压安装包 首先&#xff0c;解压下载的 .tar.xz 安装包。 cd /path/to/your/downloads tar -xvf mysql-8.0…

汽车产业数字化转型:协同创新破解挑战,平衡安全与流通

在数字经济时代的浪潮中&#xff0c;数据资源和数据信息已成为驱动各行各业转型升级的“新石油”。汽车产业&#xff0c;作为国民经济的重要支柱&#xff0c;正经历着前所未有的变革。随着数字化创新和转型的深入&#xff0c;数据在汽车全产业链中的作用和价值日益凸显。在这个…

windows 脚本批量管理上千台服务器实战案例

如果你们有接触服务器&#xff0c;都是知道服务器有BMC管理界面的&#xff0c;这几天我在做项目中&#xff0c;需要不断的开关机服务器&#xff0c;如果一两台服务器登录BMC界面重启服务器还好&#xff0c;如果服务器数量非常的庞大&#xff0c;成百上千台&#xff0c;我们不可…

yarn : 无法加载文件 C:\Users\L\AppData\Roaming\npm\yarn.ps1,因为在此系统上禁

关于执行安装yarn命令后执行yarn -v报错&#xff1a; 先确认执行安装yarn命令是否有误 # 安装yarn npm install yarn -g 终端输入set-ExecutionPolicy RemoteSigned 当然如果yarn -v仍然执行失败&#xff0c;考虑使用管理员方式运行IDEA&#xff0c; 注&#xff1a;如上操作…

Redis 数据结结构(一)—字符串、哈希表、列表

Redis&#xff08;版本7.0&#xff09;的数据结构主要包括字符串&#xff08;String&#xff09;、哈希表&#xff08;Hash&#xff09;、列表&#xff08;List&#xff09;、集合&#xff08;Set&#xff09;、有序集合&#xff08;Sorted Set&#xff09;、超日志&#xff08…

FPGA实战篇(按键控制LDE实验)

1.按键简介 按键开关是一种电子开关&#xff0c;属于电子元器件类。我们的开发板上有两种按键开关&#xff1a;第一种是本实验所使用的轻触式按键开关&#xff0c;简称轻触开关。使用时以向开关的操作方向施加压力使内部电路闭合接通&#xff0c;当撤销压力时开关断开&#xff…

4.模块化技术之子程序

总学习目录请点击下面连接 SAP ABAP开发从0到入职&#xff0c;冷冬备战-CSDN博客 目录 ​编辑 1.模块化基础和概述 使用模块化有什么好处 两大类模块化技术 程序局部的模块化 SAP系统内全局模块化 封装有什么好处&#xff1f; 2.子程序模块化 三种传递类型 子程序结构…

策略模式实战 - 猜拳游戏

**可以整体的替换一套算法&#xff0c;这就是策略模式。**这样对于同一个问题&#xff0c;可以有多种解决方案——算法实现的时候&#xff0c;可以通过策略模式来非常方便的进行算法的整体替换&#xff0c;而各种算法是独立封装好的&#xff0c;不用修改其内部逻辑。 具体的实…

新书速览|循序渐进Node.js企业级开发实践

《循序渐进Node.js企业级开发实践》 1 本书内容 《循序渐进Node.js企业级开发实践》结合作者多年一线开发实践&#xff0c;系统地介绍了Node.js技术栈及其在企业级开发中的应用。全书共分5部分&#xff0c;第1部分基础知识&#xff08;第1&#xff5e;3章&#xff09;&#xf…

通过 FRP 实现 P2P 通信:控制端与被控制端配置指南

本文介绍了如何通过 FRP 实现 P2P 通信。FRP&#xff08;Fast Reverse Proxy&#xff09;是一款高效的内网穿透工具&#xff0c;能够帮助用户突破 NAT 和防火墙的限制&#xff0c;将内网服务暴露到公网。通过 P2P 通信方式&#xff0c;FRP 提供了更加高效、低延迟的网络传输方式…

游戏发布AppStore平台

首先&#xff0c;要注册一个开发者账号。这里不多说了&#xff0c;下载官方app“Developer”&#xff0c;然后买个能发布的账号&#xff0c;个人&#x1f4b2;99的就行。&#xff08;其实还有点麻烦&#xff0c;我的好像是人脸识别后出问题了&#xff0c;反正遇到问题找苹果官方…

【Linux系列】AWK 使用指南

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

【算法】模拟

阿华代码&#xff0c;不是逆风&#xff0c;就是我疯 你们的点赞收藏是我前进最大的动力&#xff01;&#xff01; 希望本文内容能够帮助到你&#xff01;&#xff01; 目录 一&#xff1a;替换所有的问号 二&#xff1a;提莫攻击 三&#xff1a;z字形变换 四&#xff1a;外观…

Ubuntu环境安装RabbitMQ

1.安装Erlang RabbitMq需要Erlang语⾔的⽀持&#xff0c;在安装rabbitMq之前需要安装erlang # 更新软件包 sudo apt-get update # 安装 erlang sudo apt-get install erlang 查看erlang版本 : erl 退出命令:halt(). 2. 安装RabbitMQ # 更新软件包 sudo apt-get update # 安装 …

【STM32】定时器 —— 输出比较PWM

使用的单片机机型为STM32F103C8T6 文章目录 PWM输出比较编程实例输出比较呼吸灯舵机转向PWM控制直流电机 PWM 对于5V电路来说&#xff0c;输出只有高电平5V和低电平0V&#xff0c;控制LED灯就是点亮和熄灭&#xff0c;但如果想要控制其亮度呢&#xff1f;这就需要PWM PWM PWM…

Anaconda 下安装OpenCV 4.10.0

大家也可以使用pip安装。 pip install opencv-python4.10.0 这里使用conda安装 conda install opencv4.10.0 import cv2 print(cv2.__version__)

深入探索 C++ 类型转换的奥秘

目录 1. C语言中的类型转换 2.C的类型转换 &#xff08;1&#xff09;static_cast &#xff08;2&#xff09; dynamic_cast &#x1f60a;&#x1f60a;static_cast和dynamic_cast在面对继承和多态的父子类强转的区别&#xff1a; 1.static_cast 和 继承关系中的强转 …

量产小妙招---KdTreeFLANN的使用

1 概念 KDTreeFLANN是一种结合了k-d树&#xff08;k-dimensional tree&#xff09;数据结构和FLANN&#xff08;Fast Library for Approximate Nearest Neighbors&#xff09;算法库的技术&#xff0c;主要用于高效地进行最近邻搜索等操作。 KdTreeFLANN是Point Cloud Library …