OpenHarmony实战开发-手写板应用开发操作流程

分布式手写板(ArkTS)

介绍

本篇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。具体操作可参考指南《如何替换full-SDK》。
  2. 本篇Codelab使用的部分API仅系统应用可用,需要提升应用等级。具体可参考指南《访问控制授权申请指导》。

环境搭建

软件要求

  • DevEco Studio版本:DevEco Studio 4.0 Beta2。
  • OpenHarmony SDK版本:API version 10。

硬件要求

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

环境搭建

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

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

2.搭建烧录环境。

  • 完成DevEco Device Tool的安装
  • 完成RK3568开发板的烧录

3.搭建开发环境。

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

代码结构解读

本篇Codelab只对核心代码进行讲解。

├──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)}`);}}...
}

总结

您已经完成了本次Codelab的学习,并了解到以下知识点:

  1. 申请分布式相关权限的流程。
  2. 建立分布式连接的方法。
  3. Canvas组件的使用。
  4. 分布式键值数据库的使用。

为了帮助大家更深入有效的学习到鸿蒙开发知识点,小编特意给大家准备了一份全套最新版的HarmonyOS NEXT学习资源,获取完整版方式请点击→HarmonyOS教学视频

HarmonyOS教学视频

鸿蒙语法ArkTS、TypeScript、ArkUI等…视频教程

鸿蒙生态应用开发白皮书V2.0PDF:

获取完整版白皮书方式请点击→《鸿蒙生态应用开发白皮书V2.0PDF

在这里插入图片描述

鸿蒙 (Harmony OS)开发学习手册

一、入门必看

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

在这里插入图片描述


二、HarmonyOS 概念

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

在这里插入图片描述

三、如何快速入门?《鸿蒙基础入门学习指南》

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

在这里插入图片描述


四、开发基础知识

  1. 应用基础知识
  2. 配置文件
  3. 应用数据管理
  4. 应用安全管理
  5. 应用隐私保护
  6. 三方应用调用管控机制
  7. 资源分类与访问
  8. 学习ArkTS语言
  9. .……

在这里插入图片描述


五、基于ArkTS 开发

  1. Ability开发
  2. UI开发
  3. 公共事件与通知
  4. 窗口管理
  5. 媒体
  6. 安全
  7. 7.网络与链接
  8. 电话服务
  9. 数据管理
  10. 后台任务(Background Task)管理
  11. 设备管理
  12. 设备使用信息统计
  13. DFX
  14. 国际化开发
  15. 折叠屏系列
  16. .……

在这里插入图片描述


更多了解更多鸿蒙开发的相关知识可以参考:《鸿蒙 (Harmony OS)开发学习手册

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

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

相关文章

查立得源码如何去除版权

最近发现很多人百度&#xff1a;查立得源码如何去除版权。 每个源代码/软件都是有版权的&#xff0c;无法去除&#xff0c;我们也得尊重知识产权/劳动成果。 可以去除/修改的是&#xff1a;页面显示的版权信息,查立得底部信息均可自定义(一般conn.php可修改)。 另&#xff1…

【数据分析案列】--- 北京某平台二手房可视化数据分析

一、引言 本案列基于北京某平台的二手房数据&#xff0c;通过数据可视化的方式对二手房市场进行分析。通过对获取的数据进行清冼&#xff08;至关重要&#xff09;&#xff0c;对房屋价格、面积、有无电梯等因素的可视化展示&#xff0c;我们可以深入了解北京二手房市场的特点…

鸿蒙Harmony应用开发—ArkTS-枚举说明

说明&#xff1a; 本模块首批接口从API version 7开始支持&#xff0c;后续版本的新增接口&#xff0c;采用上角标单独标记接口的起始版本。 Color 从API version 9开始&#xff0c;该接口支持在ArkTS卡片中使用。 颜色名称颜色值颜色示意Black0x000000 Blue0x0000ff Brown…

大模型+强化学习_利用AI反馈扩展强化学习_RLAIF

1 2 3 4 5 6英文名称: RLAIF: Scaling Reinforcement Learning from Human Feedback with AI Feedback 中文名称: RLAIF&#xff1a;利用AI反馈扩展强化学习 链接: http://arxiv.org/abs/2309.00267v2 作者: Harrison Lee, Samrat Phatale, Hassan Mansoor, Thomas Mesnard, Jo…

链表合并--给定两个元素有序(从小到大)的链表,要求将两个链表合并成一个有序(从小到大)链表,

输入描述: 第一行输入第一个链表的结点数S1&#xff0c;S1<100。 第二行输入S1个整数&#xff0c;两两之间用空格隔开。 第三行输入第二个链表的结点数S2&#xff0c;S2<100。 第四行输入S2个整数&#xff0c;两两之间用空格隔开。 输出描述: 输出合并之后的链表结果&…

在MongoDB建模1对N关系的基本方法

“我在 SQL 和规范化数据库方面拥有丰富的经验&#xff0c;但我只是 MongoDB 的初学者。如何建立一对 N 关系模型&#xff1f;” 这是我从参加 MongoDB 分享日活动的用户那里得到的最常见问题之一。 我对这个问题没有简短的答案&#xff0c;因为方法不只有一种&#xff0c;还有…

如何使用OpenHarmony实现一个模拟应用首次启动

应用首次启动&#xff08;ArkTS&#xff09; 介绍 本篇Codelab基于自定义弹框、首选项和页面路由实现一个模拟应用首次启动的案例。需要完成以下功能&#xff1a; 实现四个页面&#xff0c;启动页、隐私协议页、广告页、应用首页。页面之间的跳转。实现自定义隐私协议弹窗&a…

是德科技N9020A信号分析仪

181/2461/8938产品概述&#xff1a; N9020A MXA信号分析仪通过增加针对新一代技术的信号分析和频谱分析能力&#xff0c;具备了中档分析仪的更高性能。它突破了以往分析仪的极限&#xff0c;支持业界更快的信号和频谱分析,实现了速度与性能的更佳优化。 速度 测试速度超过其它…

IAB欧洲发布首张泛欧洲数字零售媒体能力矩阵图

2024年1月18日&#xff0c;互动广告署-欧洲办事处&#xff08;IAB Europe)发布了首张泛欧洲数字零售媒体能力矩阵图。为媒体买家提供的新资源概述了在欧洲运营的零售商提供的现场、场外和数字店内零售媒体广告机会。 2024年1月18日&#xff0c;比利时布鲁塞尔&#xff0c;欧洲领…

docker 和K8S知识分享

docker知识&#xff1a; 比如写了个项目&#xff0c;并且在本地调试没有任务问题&#xff0c;这时候你想在另外一台电脑或者服务器运行&#xff0c;那么你需要在另外一台电脑或者服务器配置相同的软件&#xff0c;比如数据库&#xff0c;web服务器&#xff0c;必要的插件和库等…

【SysBench】OLTP 基准测试示例

前言 本文采用 MySQL 沙盒实例作为测试目标&#xff0c;使用 sysbench-1.20 对其做 OLTP 基准测试。 有关 MySQL 沙盒的更多信息&#xff0c;请参阅 玩转 MySQL Shell 沙盒实例&#xff0c;【MySQL Shell】6.8 AdminAPI MySQL 沙盒 。 1、部署一个 MySQL 沙盒实例 使用 mysq…

JVM本地方法

本地方法接口 NAtive Method就是一个java调用非java代码的接口 本地方法栈&#xff08;Native Method Statck&#xff09; Java虚拟机栈用于管理Java方法的调用&#xff0c;而本地方法栈用于管理本地方法的调用。 本地方法栈&#xff0c;也是线程私有的。 允许被实现成固定或…