HarmonyOS开发实例:【分布式手写板】

介绍

本篇Codelab使用设备管理及分布式键值数据库能力,实现多设备之间手写板应用拉起及同步书写内容的功能。操作流程:

  1. 设备连接同一无线网络,安装分布式手写板应用。进入应用,点击允许使用多设备协同,点击主页上查询设备按钮,显示附近设备。
  2. 选择设备确认,若已建立连接,启动对方设备上的手写板应用,否则提示建立连接。输入PIN码建立连接后再次点击查询设备按钮,选择设备提交,启动对方设备应用。
  3. 建立连接前绘制的内容在启动对方设备后同步,此时设备上绘制的内容会在另一端同步绘制。
  4. 点击撤销按钮,两侧设备绘制内容同步撤销。

相关概念

  • [设备管理]:模块提供分布式设备管理能力。
  • [分布式键值数据库]:分布式键值数据库为应用程序提供不同设备间数据库的分布式协同能力。

相关权限

本篇Codelab使用了设备管理及分布式键值数据库能力,需要手动替换full-SDK,并在配置文件module.json5文件requestPermissions属性中添加如下权限:

  • [分布式设备认证组网权限]:ohos.permission.ACCESS_SERVICE_DM。
  • [设备间的数据交换权限]:ohos.permission.DISTRIBUTED_DATASYNC。

约束与限制

  1. 本篇Codelab部分能力依赖于系统API,需下载full-SDK并替换DevEco Studio自动下载的public-SDK。
  2. 本篇Codelab使用的部分API仅系统应用可用,需要提升应用等级。

环境搭建

软件要求

  • [DevEco Studio]版本:DevEco Studio 4.0 Beta2。
  • OpenHarmony SDK版本:API version 10。
  • 鸿蒙指导参考:qr23.cn/AKFP8k点击或者复制转到。

搜狗高速浏览器截图20240326151547.png

硬件要求

鸿蒙HarmonyOS与OpenHarmony文档籽料+mau123789是v直接拿取

  • 开发板类型:[润和RK3568开发板]。
  • OpenHarmony系统:4.0 Release。

环境搭建

完成本篇Codelab我们首先要完成开发环境的搭建,本示例以RK3568开发板为例,参照以下步骤进行:

  1. [获取OpenHarmony系统版本]:标准系统解决方案(二进制)。以4.0 Release版本为例:

  2. 搭建烧录环境。

    1. [完成DevEco Device Tool的安装]
    2. [完成RK3568开发板的烧录]
  3. 搭建开发环境。

    1. 开始前请参考[工具准备],完成DevEco Studio的安装和开发环境配置。
    2. 开发环境配置完成后,请参考[使用工程向导]创建工程(模板选择“Empty Ability”)。
    3. 工程创建完成后,选择使用[真机进行调测]。

代码结构解读

本篇Codelab只对核心代码进行讲解,对于完整代码,我们会在gitee中提供。

├──entry/src/main/ets                 // 代码区
│  ├──common
│  │  ├──constants
│  │  │  └──CommonConstants.ets       // 公共常量类
│  │  └──utils
│  │     ├──Logger.ets                // 日志打印类
│  │     └──RemoteDeviceUtil.ets      // 设备管理类
│  ├──entryability
│  │  └──EntryAbility.ets             // 程序入口类
│  ├──pages
│  │  └──Index.ets                    // 主界面
│  ├──view
│  │  └──CustomDialogComponent.ets    // 自定义弹窗组件类
│  └──viewmodel
│     ├──KvStoreModel.ets             // 分布式键值数据库管理类
│     └──Position.ets                 // 绘制位置信息类
└──entry/src/main/resources           // 资源文件目录

界面设计

主界面由导航栏及绘制区域组成,导航栏包含撤回按钮及查询设备按钮。绘制区域使用Canvas画布组件展示绘制效果。Index.ets文件完成界面实现,使用Column及Row容器组件进行布局。

// Index.ets
let storage = LocalStorage.getShared();
@Entry(storage)
@Component
struct Index {...build() {Column() {Row() {// 撤回按钮Image($r('app.media.ic_back')).width($r('app.float.ic_back_width')).height($r('app.float.ic_back_height'))...Blank()// 查找设备按钮Image($r('app.media.ic_hop')).width($r('app.float.ic_hop_width')).height($r('app.float.ic_hop_height'))...}.width(CommonConstants.FULL_PERCENT).height(CommonConstants.TITLE_HEIGHT)Row() {// 绘制区域Canvas(this.canvasContext).width(CommonConstants.FULL_PERCENT).height(CommonConstants.FULL_PERCENT)...}....width(CommonConstants.FULL_PERCENT).layoutWeight(CommonConstants.NUMBER_ONE)}.height(CommonConstants.FULL_PERCENT).width(CommonConstants.FULL_PERCENT)}...
}

分布式组网

准备分布式环境

创建设备管理器。设备管理器创建完成后注册设备上线离线监听,信任设备上线离线时触发。执行获取本地设备信息,获取信任设备列表,初始化展示设备列表等方法。其中deviceManager类需使用full-SDK。

// RemoteDeviceUtil.ets
import deviceManager from '@ohos.distributedHardware.deviceManager';class RemoteDeviceUtil {...async createDeviceManager() {...await new Promise((resolve: (value: Object | PromiseLike<Object>) => void, reject: ((reason?: RejectError) => void)) => {try {// 创建设备管理器deviceManager.createDeviceManager(CommonConstants.BUNDLE_NAME,(error, value: deviceManager.DeviceManager) => {...this.myDeviceManager = value;// 注册信任设备上线离线监听this.registerDeviceStateListener();// 获取本地设备信息this.getLocalDeviceInfo();// 获取信任设备列表this.getTrustedDeviceList();// 初始化展示设备列表this.initDeviceList();resolve(value);});} catch (error) {Logger.error('RemoteDeviceModel',`createDeviceManager failed, error=${JSON.stringify(error)}`);}});}...
}

注册设备状态监听。已验证设备上线或有新设备验证通过时状态类型为ONLINE,将设备添加至信任设备列表。设备离线时状态类型为OFFLINE,将设备从信任列表中移除。

// RemoteDeviceUtil.ets
class RemoteDeviceUtil {...// 注册设备状态改变监听registerDeviceStateListener(): void {...try {// 注册监听this.myDeviceManager.on('deviceStateChange', (data) => {...switch (data.action) {// 设备上线case deviceManager.DeviceStateChangeAction.ONLINE: {this.deviceStateChangeActionOnline(data.device);break;}// 设备离线case deviceManager.DeviceStateChangeAction.OFFLINE: {this.deviceStateChangeActionOffline(data.device);break;}...}});} catch (error) {Logger.error('RemoteDeviceModel',`registerDeviceStateListener on('deviceStateChange') failed, error=${JSON.stringify(error)}`);}}// 设备上线,加入信任列表及展示列表deviceStateChangeActionOnline(device: deviceManager.DeviceInfo): void {this.trustedDeviceList[this.trustedDeviceList.length] = device;this.addToDeviceList(device);}// 设备下线,将设备移出信任列表和展示列表deviceStateChangeActionOffline(device: deviceManager.DeviceInfo): void {let list: deviceManager.DeviceInfo[] = [];for (let i: number = 0; i < this.trustedDeviceList.length; i++) {if (this.trustedDeviceList[i].networkId !== device.networkId) {list.push(this.trustedDeviceList[i]);continue;}}this.deleteFromDeviceList(device);this.trustedDeviceList = list;}...
}

建立分布式连接

点击主界面的查询设备按钮,执行发现设备方法,注册设备发现监听任务,同时拉起弹窗展示设备列表。当弹窗关闭时,执行停止发现设备方法,注销监听任务。

// RemoteDeviceUtil.ets
class RemoteDeviceUtil {...// 处理新发现的设备deviceFound(data: DeviceInfoInterface): void {for (let i: number = 0; i < this.discoverList.length; i++) {if (this.discoverList[i].deviceId === data.device.deviceId) {Logger.info('RemoteDeviceModel', `deviceFound device exist=${JSON.stringify(data)}`);return;}}this.discoverList[this.discoverList.length] = data.device;this.addToDeviceList(data.device);}startDeviceDiscovery(): void {...try {// 注册发现设备监听this.myDeviceManager.on('deviceFound', (data) => {...// 处理发现的设备this.deviceFound(data);});...let info: deviceManager.SubscribeInfo = {subscribeId: this.subscribeId,mode: CommonConstants.SUBSCRIBE_MODE,medium: CommonConstants.SUBSCRIBE_MEDIUM,freq: CommonConstants.SUBSCRIBE_FREQ,isSameAccount: false,isWakeRemote: true,capability: CommonConstants.SUBSCRIBE_CAPABILITY};// 发现周边设备this.myDeviceManager.startDeviceDiscovery(info);} catch (error) {Logger.error('RemoteDeviceModel',`startDeviceDiscovery failed error=${JSON.stringify(error)}`);}}// 停止发现设备stopDeviceDiscovery(): void {...try {// 停止发现设备this.myDeviceManager.stopDeviceDiscovery(this.subscribeId);// 注销监听任务this.myDeviceManager.off('deviceFound');this.myDeviceManager.off('discoverFail');} catch (error) {Logger.error('RemoteDeviceModel',`stopDeviceDiscovery failed error=${JSON.stringify(error)}`);}}...
}

选择弹窗内的设备项提交后,执行设备验证。

  1. 若设备在信任设备列表,执行startAbility()方法启动连接设备上的应用,将当前的绘制信息作为参数发送至连接设备。
  2. 若设备不是信任设备,执行authenticateDevice()方法启动验证。此时连接设备提示是否接受,接收连接后连接设备展示PIN码,本地设备输入PIN码确认后连接成功。再次点击查询设备按钮,选择已连接设备,点击确认启动连接设备上的应用。
// RemoteDeviceUtil.ets
class RemoteDeviceUtil {...// 设备验证authenticateDevice(context: common.UIAbilityContext,device: deviceManager.DeviceInfo,positionList: Position[]): void {// 设备为信任设备,启动连接设备上的应用let tmpList = this.trustedDeviceList.filter((item: deviceManager.DeviceInfo) => device.deviceId === item.deviceId);if (tmpList.length > 0) {this.startAbility(context, device, positionList);return;}...try {// 执行设备认证,启动验证相关弹窗,接受信任,显示PIN码,输入PIN码等this.myDeviceManager.authenticateDevice(device, authParam, (err) => {...})} catch (error) {Logger.error('RemoteDeviceModel',`authenticateDevice failed error=${JSON.stringify(error)}`);}}// 启动连接设备上的应用startAbility(context: common.UIAbilityContext, device: deviceManager.DeviceInfo, positionList: Position[]): void {...// 启动连接设备上的应用context.startAbility(wantValue).then(() => {Logger.info('RemoteDeviceModel', `startAbility finished wantValue=${JSON.stringify(wantValue)}`);}).catch((error: Error) => {Logger.error('RemoteDeviceModel', `startAbility failed, error=${JSON.stringify(error)}`);})}...
}

资源释放

程序关闭时,注销设备状态监听任务,并释放DeviceManager实例。

// RemoteDeviceUtil.ets
class RemoteDeviceUtil {...// 注销监听任务unregisterDeviceListCallback(): void {...try {// 注销设备状态监听this.myDeviceManager.off('deviceStateChange');// 释放DeviceManager实例this.myDeviceManager.release();} catch (err) {Logger.error('RemoteDeviceModel',`unregisterDeviceListCallback stopDeviceDiscovery failed, error=${JSON.stringify(err)}`);}}...
}

绘制功能

Canvas组件区域监听触摸事件,按照按下、移动、抬起等触摸事件,记录绘制的起点、中间点以及终点。触摸事件触发时,使用CanvasRenderingContext2D对象的绘制方法根据位置信息进行绘制。绘制结束后,将当前位置信息列表存入分布式键值数据库。

// Index.ets
let storage = LocalStorage.getShared();
@Entry(storage)
@Component
struct Index {...  build() {Column() {...Row() {Canvas(this.canvasContext)...}.onTouch((event: TouchEvent) => {this.onTouchEvent(event);})...}...}// 绘制事件onTouchEvent(event: TouchEvent): void {let positionX: number = event.touches[0].x;let positionY: number = event.touches[0].y;switch (event.type) {// 手指按下case TouchType.Down: {this.canvasContext.beginPath();this.canvasContext.lineWidth = CommonConstants.CANVAS_LINE_WIDTH;this.canvasContext.lineJoin = CommonConstants.CANVAS_LINE_JOIN;this.canvasContext.moveTo(positionX, positionY);this.pushData(true, false, positionX, positionY);break;}// 手指移动case TouchType.Move: {this.canvasContext.lineTo(positionX, positionY);this.pushData(false, false, positionX, positionY);break;}// 手指抬起case TouchType.Up: {this.canvasContext.lineTo(positionX, positionY);this.canvasContext.stroke();this.pushData(false, true, positionX, positionY);break;}default: {break;}}}pushData(isFirstPosition: boolean, isEndPosition: boolean, positionX: number, positionY: number): void {let position = new Position(isFirstPosition, isEndPosition, positionX, positionY);// 存入位置信息列表this.positionList.push(position);if (position.isEndPosition) {// 当前位置为终点时,将位置信息列表存入分布式键值数据库this.kvStoreModel.put(CommonConstants.CHANGE_POSITION, JSON.stringify(this.positionList));}}...
}

点击撤销按钮时,从位置列表中后序遍历移除位置信息,直到找到轨迹的初始位置,完成移除上一次绘制的轨迹。移除完成后将位置信息列表存入分布式键值数据库中。执行redraw()方法,清空画板上的内容,遍历位置信息列表,重新绘制。

// Index.ets
let storage = LocalStorage.getShared();
@Entry(storage)
@Component
struct Index {...@LocalStorageProp('positionList') positionList: Position[] = [];...build() {Column() {Row() {// 撤销按钮Image($r('app.media.ic_back')).width($r('app.float.ic_back_width')).height($r('app.float.ic_back_height')).margin({ left: CommonConstants.ICON_MARGIN_LEFT }).onClick(() => {this.goBack();})...}.width(CommonConstants.FULL_PERCENT).height(CommonConstants.TITLE_HEIGHT)...}...redraw(): void {// 删除画布内的绘制内容this.canvasContext.clearRect(0, 0, this.canvasContext.width, this.canvasContext.height);// 使用当前记录的位置信息,重新绘制this.positionList.forEach((position) => {...if (position.isFirstPosition) {this.canvasContext.beginPath();this.canvasContext.lineWidth = CommonConstants.CANVAS_LINE_WIDTH;this.canvasContext.lineJoin = CommonConstants.CANVAS_LINE_JOIN;this.canvasContext.moveTo(position.positionX, position.positionY);} else {this.canvasContext.lineTo(position.positionX, position.positionY);if (position.isEndPosition) {this.canvasContext.stroke();}}});}// 撤回上一笔绘制goBack(): void {if (this.positionList.length === 0) {return;}// 移除位置信息直到位置起始位置for (let i: number = this.positionList.length - 1; i >= 0; i--) {let position: Position | undefined = this.positionList.pop();if (position !== undefined && position.isFirstPosition) {break;}}this.redraw();this.kvStoreModel.put(CommonConstants.CHANGE_POSITION, JSON.stringify(this.positionList));}...
}

分布式键值数据库

使用分布式键值数据库需申请数据交换权限:ohos.permission.DISTRIBUTED_DATASYNC。

应用启动时创建分布式键值数据库,设置数据库数据改变监听。数据改变时执行回调,获取插入或更新数据列表,遍历列表,匹配位置信息列表的设置key,更新位置列表后重新绘制。

// Index.ets
...
import KvStoreModel from '../viewmodel/KvStoreModel';
...
let storage = LocalStorage.getShared();
@Entry(storage)
@Component
struct Index {...private kvStoreModel: KvStoreModel = new KvStoreModel();...aboutToAppear() {...this.createKVStore();}...createKVStore(): void {// 创建分布式键值数据库this.kvStoreModel.createKvStore(this.context, (data: distributedKVStore.ChangeNotification) => {// 使用分布式键值数据库内的内容重置位置信息列表this.positionList = [];let entries: distributedKVStore.Entry[] = data.insertEntries.length > 0 ? data.insertEntries : data.updateEntries;entries.forEach((entry: distributedKVStore.Entry) => {if (CommonConstants.CHANGE_POSITION === entry.key) {this.positionList = JSON.parse((entry.value.value) as string);// 位置信息列表更新后,重新绘制this.redraw();}});});}...
}

创建分布式键值数据库。设置数据库类型为KVStoreType.SINGLE_VERSION单版本数据库,其他配置参考[创建数据库配置信息]。创建数据库成功后,调用enableSync()方法开启同步,调用setDataChangeListener()方法订阅数据变更通知。

// KvStoreModel.ets
export default class KvStoreModel {...kvStore?: distributedKVStore.SingleKVStore;...createKvStore(context: common.UIAbilityContext,callback: (data: distributedKVStore.ChangeNotification) => void): void {...try {// 创建一个KVManager对象实例,用于管理数据库对象this.kvManager = distributedKVStore.createKVManager(config);} catch (error) {Logger.error('KvStoreModel',`createKvStore createKVManager failed, err=${JSON.stringify(error)}`);return;}// 创建数据库的配置信息let options: distributedKVStore.Options = {...kvStoreType: distributedKVStore.KVStoreType.SINGLE_VERSION...};// 获取分布式键值数据库this.kvManager.getKVStore(CommonConstants.KVSTORE_ID, options).then((store: distributedKVStore.SingleKVStore) => {...this.kvStore = store;// 开启同步this.kvStore.enableSync(true).then(() => {Logger.info('KvStoreModel', 'createKvStore enableSync success');}).catch((error: Error) => {Logger.error('KvStoreModel',`createKvStore enableSync fail, error=${JSON.stringify(error)}`);});this.setDataChangeListener(callback);}).catch((error: Error) => {Logger.error('getKVStore',`createKvStore getKVStore failed, error=${JSON.stringify(error)}`);})}...
}

订阅数据变更通知。创建分布式键值数据库,设置数据变更订阅,订阅类型为全部,当更新数据集或插入数据集大于0时,执行传入的callback()方法。

// KvStoreModel.ets
export default class KvStoreModel {...kvStore?: distributedKVStore.SingleKVStore;...setDataChangeListener(callback: (data: distributedKVStore.ChangeNotification) => void): void {...try {// 订阅数据变更通知this.kvStore.on('dataChange', distributedKVStore.SubscribeType.SUBSCRIBE_TYPE_ALL,(data: distributedKVStore.ChangeNotification) => {if ((data.updateEntries.length > 0) || (data.insertEntries.length > 0)) {callback(data);}});} catch (error) {Logger.error('KvStoreModel',`setDataChangeListener on('dataChange') failed, err=${JSON.stringify(error)}`);}}...
}

应用退出时,分布式键值数据库取消数据改变监听。

// Index.ets
...
import KvStoreModel from '../viewmodel/KvStoreModel';
...
let storage = LocalStorage.getShared();
@Entry(storage)
@Component
struct Index {...private kvStoreModel: KvStoreModel = new KvStoreModel();...aboutToDisappear() {this.kvStoreModel.removeDataChangeListener();}...
}// KvStoreModel.ets
export default class KvStoreModel {...kvStore?: distributedKVStore.SingleKVStore;...removeDataChangeListener(): void {...try {// 取消数据改变监听this.kvStore.off('dataChange');} catch (error) {Logger.error('KvStoreModel',`removeDataChangeListener off('dataChange') failed, err=${JSON.stringify(error)}`);}}...
}

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

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

相关文章

JVM之方法区的详细解析

方法区 方法区&#xff1a;是各个线程共享的内存区域&#xff0c;用于存储已被虚拟机加载的类信息、常量、即时编译器编译后的代码等数据&#xff0c;虽然 Java 虚拟机规范把方法区描述为堆的一个逻辑部分&#xff0c;但是也叫 Non-Heap&#xff08;非堆&#xff09; 设置方法…

Latex学习(从入门到入土)3

第一章&#xff1a;排版矩阵 在LaTeX中&#xff0c;排版矩阵可以使用amsmath宏包提供的几种不同环境来实现。以下是一些常用的环境和它们的使用方式&#xff1a; ### 使用matrix环境 matrix环境是最基础的矩阵环境&#xff0c;它创建一个带有边框的矩阵&#xff0c;元素默认…

精益思维驱动人工智能革新:理论到实践的跃迁之旅

随着科技的飞速发展&#xff0c;人工智能&#xff08;AI&#xff09;已成为引领未来的关键力量。在这个变革的时代&#xff0c;如何将精益思维与人工智能相结合&#xff0c;推动AI从理论走向实践&#xff0c;成为行业内外关注的焦点。本文&#xff0c;天行健精益生产顾问将分享…

Macbook M1 Pro使用brew安装Docker并安装Nacos【超详细图解】

目录 一、安装 Docker 二、修改 Docker 镜像地址 三、拉取镜像-举例 Nacos 1.拉取镜像 2.查看本地镜像 3.删除镜像 四、启动容器 1.启动 Nacos 容器&#xff1a; I.方式一【推荐】 II.方式二【懒人推荐】 2.访问 Nacos Web 控制台 3.进入容器和退出容器 五、配置…

麻将的玩法及技巧

麻将是一种古老而受欢迎的博弈游戏&#xff0c;其玩法和技巧多种多样&#xff0c;因地区和玩家习惯的不同而有所差异。以下是一些通用的麻将玩法和技巧&#xff1a; 一、玩法 洗牌与码牌&#xff1a;玩家双手搓动牌&#xff0c;使牌均匀而无序地运动&#xff0c;称为“洗牌”。…

Java处理CSV类库:OpenCSV

一&#xff1a;CSV简介 Comma-Separated Values(CSV), 因分隔符没有严格指定规范标准&#xff0c;可以使用逗号&#xff0c;也可以使用其他字符&#xff08;如制表符\t、分号;等&#xff09;&#xff0c;所以CSV也称为 逗号分隔值或者字符分隔值。csv文件是使用纯文本来存储表…

MSSQL 命令行操作说明 sql server 2022 命令行下进行配置管理

说明&#xff1a;本文的内容是因为我在导入Access2019的 *.accdb 格式的数据时&#xff0c;总是出错的背景下&#xff0c;不得已搜索和整理了一下&#xff0c;如何用命令行进行sql server 数据库和用户管理的方法&#xff0c;作为从Access2019 直接导出数据到sql server 数据库…

linux限权

shell命令以及运行原理 什么是shell命令&#xff1a; 将使用者的命令翻译给核心&#xff08;kernel&#xff09;处理。同时&#xff0c;将核心的处理结果翻译给使用者。 shell就相当于操作系统外的一层外壳 其实就是登录linux时的一个可执行程序&#xff08;进程&#xff09…

安装kali虚拟机

第一步&#xff1a;安装vmware 安装到最后&#xff0c;会提示输入序列号 安装成功后的图标 第二步&#xff1a;创建一个虚拟主机 创建了一个虚拟机 第三步&#xff1a;给虚拟机安装系统 安装系统&#xff0c;都必须有 系统镜像文件 一般系统镜像文件的后缀是 .iso 第一步&a…

利用CNN-Bigru-Attention模型输电线路故障诊断(Python代码,TensorFlow框架,)

效果视频&#xff1a;利用CNN-Bigru-Attention模型输电线路故障诊断(Python代码&#xff0c;TensorFlow框架&#xff0c;压缩包带有数据集和代码&#xff0c;解压缩可直接运行)_哔哩哔哩_bilibili 售后包免费远程协助运行&#xff08;用向日葵或者todesk软件协助&#xff09; …

GoogleNet网络训练集和测试集搭建

测试集和训练集都是在之前搭建好的基础上进行修改的&#xff0c;重点记录与之前不同的代码。 还是使用的花分类的数据集进行训练和测试的。 一、训练集 1、搭建网络 设置参数&#xff1a;使用辅助分类器&#xff0c;采用权重初始化 net GoogleNet(num_classes5, aux_logi…

Midjourney常见玩法及prompt关键词技巧

今天系统给大家讲讲Midjourney的常见玩法和prompt关键词的一些注意事项&#xff0c;带大家入门&#xff5e;&#xff08;多图预警&#xff0c;建议收藏&#xff5e;&#xff09; 一、入门及常见玩法 1、注册并添加服务器&#xff08;会的童鞋可跳过&#xff5e;&#xff09; …

实在智能携AI Agent智能体亮相2024年度QCon全球软件开发大会

4月11日-13日&#xff0c;以“全面进化”作为2024年度主题的「QCon全球软件开发大会暨智能软件开发生态展」在北京隆重举行。作为AI准独角兽和超自动化头部企业&#xff0c;实在智能应邀出席发表《面向办公自动化领域的AI Agent建设思考与分享》演讲及圆桌交流&#xff0c;展示…

反转二叉树(力扣226)

解题思路&#xff1a;用队列进行前序遍历的同时把节点的左节点和右节点交换 具体代码如下&#xff1a; class Solution { public:TreeNode* invertTree(TreeNode* root) {if (root NULL) return root;swap(root->left, root->right); // 中invertTree(root->left)…

OpenHarmony南向开发案例【智慧中控面板(基于 Bearpi-Micro)】

1 开发环境搭建 【从0开始搭建开发环境】【快速搭建开发环境】 参考鸿蒙开发指导文档&#xff1a;gitee.com/li-shizhen-skin/harmony-os/blob/master/README.md点击或复制转到。 【注意】&#xff1a;快速上手教程第六步出拉取代码时需要修改代码仓库地址 在MobaXterm中输入…

【模拟】Leetcode 提莫攻击

题目讲解 495. 提莫攻击 算法讲解 前后的两个数字之间的关系要么是相减之差 > 中毒时间 &#xff0c;要么反之 那即可通过示例&#xff0c;进行算法的模拟&#xff0c;得出上图的计算公式 class Solution { public:int findPoisonedDuration(vector<int>& time…

至少需要[XXXXMB]内存才能安装(宝塔导入数据库提示)

①我的2g内存腾讯云服务器想安装mysql8.0 ②宝塔提示“至少需要[3700MB]内存才能安装” 将数据库部署到宝塔上的时候提示-----》至少需要[XXXXMB]内存才能安装&#xff0c;解决的方法其实也很简单。 首先&#xff0c;进入文件夹/www/server/panel/class&#xff0c;找到找到…

PostgreSQL的学习心得和知识总结(一百三十八)|深入理解PostgreSQL数据库之Protocol message构造和解析逻辑

目录结构 注&#xff1a;提前言明 本文借鉴了以下博主、书籍或网站的内容&#xff0c;其列表如下&#xff1a; 1、参考书籍&#xff1a;《PostgreSQL数据库内核分析》 2、参考书籍&#xff1a;《数据库事务处理的艺术&#xff1a;事务管理与并发控制》 3、PostgreSQL数据库仓库…

【Java开发指南 | 第十一篇】Java运算符

读者可订阅专栏&#xff1a;Java开发指南 |【CSDN秋说】 文章目录 算术运算符关系运算符位运算符逻辑运算符赋值运算符条件运算符&#xff08;?:&#xff09;instanceof 运算符Java运算符优先级 Java运算符包括&#xff1a;算术运算符、关系运算符、位运算符、逻辑运算符、赋值…

Postman之版本信息查看

Postman之版本信息查看 一、为何需要查看版本信息&#xff1f;二、查看Postman的版本信息的步骤 一、为何需要查看版本信息&#xff1f; 不同的版本之间可能存在功能和界面的差异。 二、查看Postman的版本信息的步骤 1、打开 Postman 2、打开设置项 点击页面右上角的 “Set…