HarmonyOS开发 - 本地持久化之实现LocalStorage实例
用户首选项为应用提供Key-Value键值型的数据处理能力,支持应用持久化轻量级数据,并对其修改和查询。数据存储形式为键值对,键的类型为字符串型,值的存储数据类型包括数字型、字符型、布尔型以及这3种类型的数组类型。
说明:
本模块首批接口从API version 9开始支持。后续版本的新增接口,采用上角标单独标记接口的起始版本。首选项无法保证进程并发安全,会有文件损坏和数据丢失的风险,不支持在多进程场景下使用。
对于前端的朋友,可能使用像uni-app、小程序或web端中的本地持久化API习惯了,也能像使用sessionStorage或localStorage一样便捷。我们也可以利用HarmonyOS中API提供的用户首选项的功能,来实现LocalStorage实例。
上述说明了用户首选项只能存储数字型、字符型、布尔型这3种数据类型,在对Preference实例封装过程中,在基础上再增加对json对象数据存储能力,并且实现数据缓存具有时效性等功能。
一、用户首选项
1.1、导入模块
import preferences from '@ohos.data.preferences'
1.2、获取实例
获取Preferences实例,使用Promise异步回调(当然,获取实例方法也可以使用callback异步回调)。
// context为应用的上下文
preferences.getPreferences(context, 'application').then(preference => {// preference 实例名称
}).catch(err => {// error message
})
参数说明:
参数名 | 类型 | 是否必填 | 描述 |
---|---|---|---|
context | Context | 是 | 应用上下文。 |
name | string | 是 | Preferences实例的名称。 |
callback | AsyncCallback<Preferences> | 是 | 回调函数。当获取Preferences实例成功,err为undefined,返回Preferences实例;否则err为错误对象。 |
1.3、Preferences实例中的方法
这些操作通常都是异步的,因此你需要使用Promise或者async/await来处理异步逻辑。
使用callback异步回调的方法:
方法名 | 参数 | 返回值 | 描述 |
---|---|---|---|
get() | key: string, defValue: ValueType, callback: AsyncCallback<ValueType> | void | 从缓存的Preferences实例中获取键对应的值,如果值为null或者非默认值类型,返回默认数据defValue。 |
getAll() | callback: AsyncCallback<Object> | void | 从缓存的Preferences实例中获取所有键值数据。 |
put() | key: string, value: ValueType, callback: AsyncCallback<void> | void | 将数据写入缓存的Preferences实例中,可通过flushflush将Preferences实例持久化。 |
has() | key: string, callback: AsyncCallback<boolean> | void | 检查缓存的Preferences实例中是否包含名为给定Key的存储键值对。 |
delete() | key: string, callback: AsyncCallback<void> | void | 从缓存的Preferences实例中删除名为给定Key的存储键值对,可通过flush将Preferences实例持久化。 |
flush() | callback: AsyncCallback<void> | void | 将缓存的Preferences实例中的数据异步存储到用户首选项的持久化文件中。 |
clear() | callback: AsyncCallback<void> | void | 清除缓存的Preferences实例中的所有数据,可通过flush将Preferences实例持久化。 |
例如获取示例,代码如下:
// 获取对应键对应的值,默认为空
this.preference.get(key, '', (err, value) => {if(err) {console.log('preference error', err)}
})
使用Promise异步回调的方法:
方法名 | 参数 | 返回值 | 描述 |
---|---|---|---|
get() | key: string, defValue: ValueType | Promise<ValueType> | 从缓存的Preferences实例中获取键对应的值,如果值为null或者非默认值类型,返回默认数据defValue |
getAll() | / | Promise<Object> | 从缓存的Preferences实例中获取所有键值数据。 |
put() | key: string, value: ValueType | Promise<void> | 将数据写入缓存的Preferences实例中,可通过flushflush将Preferences实例持久化 |
has() | key: string | Promise<boolean> | 检查缓存的Preferences实例中是否包含名为给定Key的存储键值对。 |
delete() | key: string | Promise<void> | 从缓存的Preferences实例中删除名为给定Key的存储键值对,可通过flush将Preferences实例持久化。 |
flush() | / | Promise<void> | 将缓存的Preferences实例中的数据异步存储到用户首选项的持久化文件中。 |
clear() | / | Promise<void> | 清除缓存的Preferences实例中的所有数据,可通过flush将Preferences实例持久化。 |
例如添加示例,代码如下:
// 添加数据
this.preference.put(key, value).then(() => {this.preference.flush()
}).catch(e => {console.log('preference error', e)
})
二、封装LocalStorage
在封装LocalStorage类之前,首先在ets目录下创建utils文件夹,并且在该文件夹下创建LocalStorage类文件。如下图:
2.1定义LocalStorage类
定义LocalStorage类,需要先做好几下几步准备:
- 获取Preferences实例对象,需要传入UIAbility组件的应用上下文,可通过"@ohos.app.ability.common"模块获取UIAbility类型,并将其赋给context形参。
- 定义私有preference变量,用于接收获取到的Preferences实例,后期可以直接调用,无需重复获取。
- 定义存储值类型vueType,根据用户首选项描述已知,其只能为number、string和boolean三种类型。
- 最后将LocalStorage类实例对象导出,作为单例模式导出。
import common from '@ohos.app.ability.common'
import preferences from '@ohos.data.preferences'// 定义存储值类型
type valueType = string | number | boolean/*** 定义LocalStorage类*/
class LocalStorage {private preference: preferences.Preferences // 定义首选项实例对象
}
/*** 实例LocalStorage*/
const localStorage = new LocalStorage()/*** 导出localStorage单例对象*/
export default localStorage as LocalStorage
2.2 初始化并获取Preferences实例
类定义好后,在类体内定义initial()函数,用来初始化并获取Preferences实例,后期LocalStorage类中方法直接使用preference对象即可。在获取Prefences实例时,需要给实例命名,这里则直接使用“应用上下文”的moduleName名称,即您定义项目时取的模块名称。代码如下:
// 定义初始化函数
initial(context: common.UIAbilityContext): void {// 这里将UIAbility中应用上下文的moduleName作用为实例名称,即该项目的applicationpreferences.getPreferences(context, context.abilityInfo.moduleName).then(preference => {this.preference = preference // 将获取实例赋值给私有变量console.log('preference success~')}).catch(e => {console.log('preference error', e)})
}
这里使用Promise异步回调获取Preferences实例,如果习惯使用async/await朋友可以将其简单修改下即可,代码如下:
async initial(context: common.UIAbilityContext): void {// 这里将UIAbility中应用上下文的moduleName作用为实例名称,即该项目的applicationtry {this.preference = await preferences.getPreferences(context, context.abilityInfo.moduleName)} catch (e) {console.log('preference error', e)}
}
2.3 UIAbility中初始化
当initial()函数定义好后,我们将要在什么地方调用并初始化,并且context: common.UIAbilityContext中的应用上下文在哪获取? 我们可以在项目中打开Ability文件,会发现该类是继承UIAblity,所以该类也继承了父类的context应用上下文,onCreate()函数优先于页面之前,所以将initial()放到该方法中即可,并且通过this可直接过去context,将其放到initial()方法参数中。
如果有朋友还在疑虑类中是否存在context问题,可以打开UIAblity模块,可以看到其类中定义了context变量,如下图:
ApplicationAblity继承了UIAbility,所以我们在类中可以直接调用到context应用上下文。代码如下:
import UIAbility from '@ohos.app.ability.UIAbility';
import hilog from '@ohos.hilog';
import window from '@ohos.window';
import LocalStorage from '../utils/LocalStorage'export default class ApplicationAbility extends UIAbility {onCreate(want, launchParam) {hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate');// 初始化本地存储LocalStorage.initial(this.context)}// 略...}
当LocalStorage类中initial()函数执行后,单例对象中的preferences实例也成功创建并且初始化。
注:如果你的Ability文件是ts文件,将其后缀改为ets即可,因为ets文件无法引入ts文件。
2.4 引用LocalStorage单例
此时,我们在首页中引入LocalStorage实例,完成对数据的增、删、改、查等操作。
打开pages/index.ets,添加一些相关操作功能按钮,代码如下:
import localStorage from '../utils/LocalStorage'
let index = 0
@Entry
@Component
struct Index {@State message: string = ''// 重置内容renderMsg(message: string | number | boolean){this.message = message.toString()}build() {Row() {Column() {Row(){Text(this.message || '-').fontSize(50).fontWeight(FontWeight.Bold)}.width('100%').height('150vp')Row(){// 添加相关操作按钮Button('添加').onClick(() => {})Button('读取').onClick(() => {})Button('删除').onClick(() => {})Button('清空').onClick(() => {})}.width('100%').justifyContent(FlexAlign.Center)}.width('100%')}.height('100%').alignItems(VerticalAlign.Top)}
}
页面效果如下图:
2.5 添加数据
打开utils\LocalStorage.ets文件,在类中添加put()函数,增加添加数据功能;前面执行initial()方法时,Preferences实例已初始过了,所以在类中函数直接调用即可;并且当通过Preferences实例成功添加数据后,需要调用flush()函数刷新下。代码如下:
/*** 定义增加函数* @param key* @param value*/put(key: string, value: valueType ): void {this.preference.put(key, value).then(() => this.preference.flush()).catch(e => {console.log('testTag error', e)})}
打开pages\index.ets文件,执行添加数据,代码如下:
// 添加相关操作按钮
Button('添加').onClick(() => {localStorage.put('indexValue', ++index)this.renderMsg('add:' + index)console.log('testTag', index)
})
点击添加按钮后,数据则通过Preferences实例保存到本地,页面呈现添加数据。如下图:
有些朋友可能喜欢使用async/await写法,只需简单修改下即可。
utils/LocalStorage.ets文件中代码修改如下:
async put(key: string, value: valueType) {try {await this.preference.put(key, value)await this.preference.flush()} catch (e) {console.log('testTag error', e)}
}
pages/index.ets文件中代码修改如下:
Button('添加').onClick(async () => {await localStorage.put('indexValue', ++index)this.renderMsg('add:' + index)console.log('testTag', index)
})
此时点击添加按钮,一样可以实现数据本地化存储。
2.6 获取数据
获取比较简单了,打开utils/LocalStorage.ets文件,在类中增加获取数据功能。这里get()函数中第二个默认值给空即可,代码如下:
/*** 定义获取对应key数据* @param key*/
getValue(key: string): Promise<valueType> {return this.preference.get(key, '') as Promise<valueType>
}
在首页获取数据事件中,添加getValue()函数,获取缓存的数据。代码如下:
Button('读取').onClick(() => {localStorage.getValue('indexValue').then(value => {this.renderMsg('get:' + value)console.log('testTag', value)}).catch(err => {console.log('testTag error', err)})
})
将获取到的数据显示到界面中,则拿到的是上次缓存的数据。如下图:
2.7 修改数据
修改数据,直接使用put()函数,将数据重置即可。也可以LocalStorage类中添加update()函数,判断传入的内容与上次缓存一致,则不执行Preferences实例中put()方法。
打开utils\LocalStorage.ets文件,在类中添加update()方法, 代码如下:
/**
* 更新数据
* @param key
* @param value
*/
async update(key: string, value: valueType){try {const preValue = await this.getValue(key)// 当更新内容与上次不一致时,修改数据if(preValue != value) {this.put(key, value)}} catch (e) {console.log('testTag error', e)}
}
2.8 移除数据
移除指定数据,则通过Preferences实例中的delete()函数,打开utils\LocalStorage.ets文件,代码如下:
/*** 定义移除函数* @param key*/
remove(key: string): void {this.preference.delete(key).then(() => this.preference.flush()).catch(e => {console.log('testTag error', e)})
}
打开pages\index.ets文件,在删除事件中添加移除功能,然后再通过get()函数重新获取本地存储的indexValue内容,代码如下:
Button('删除').onClick(async () => {// 移除indexValue键对应的值localStorage.remove('indexValue')// 重新获取indexValueconst value = await localStorage.getValue('indexValue')this.renderMsg('delete:' + value)console.log('testTag delete', value) // delete:
})
先添加数据,然后执行删除事件,页面效果如下:
2.9 清空数据
清空数据则是将当前Preferences实例中,装饰所有键-值对内容进行清空,代码如下:
/*** 定义清除所有数据函数*/
clearAll(): void {this.preference.clear().then(() => this.preference.flush()).catch(e => {console.log('testTag error', e)})
}
三、保存JSON对象数据
例如要保存 {"name":"Tom","age":18} 结构的JSON对象数据,则需要在put()函数中将其转换为string类型数据,再将其进行保存;在获取时候,在getValue()方法中,识别并将其转换为JSON对象模式。
3.1 判断字符串是否为JSON
首先打开utils/utils.ets,在该文件中添加判断字符串数据是否为object对象的函数;如果你的项目中未创建该文件,创建它即可。代码如下:
/*** 判断字符串是否为JSON对象*/
export const isJsonObject = (value: string) : boolean => {try {const parseStr = JSON.parse(value)return 'object' === typeof parseStr && null !== parseStr} catch (e) {console.log('testTag', e)return false}
}
3.2 修改put()函数
打开utils/LocalStorage.ets文件,先修改put()函数,让其支持存储json对象数据,代码如下:
put(key: string, value: valueType | object ): void {// 如果数据为object类型,将其转换为字符串类型数据进行存储if('object' === typeof value) {value = JSON.stringify(value)}this.preference.put(key, value).then(() => this.preference.flush()).catch(e => {console.log('testTag error', e)})
}
3.3 修改getValue()函数
在修改getValue()函数时,使用async/await写法当示例,显示更为简洁,代码如下:
async getValue(key: string): Promise<valueType> {let value = (await this.preference.get(key, '')) as valueType// 判断如果为字符串类型数据,并且为JSON对象格式数据,将其转换为对象if('string' === typeof value && isJsonObject(value)) {try {value = JSON.parse(value)} catch (e) {value = nullconsole.log('testTag error', e)}}// 重新通过Promise异步回调将结果返回return Promise.resolve(value)
}
3.4 展示json存储能力
当上述代码完成后,在添加按钮事件位置,将之前存储number数据更改为json数据,进行存储,再来看看效果。
打开pages/index.est文件,更新添加事件,代码如下:
Button('添加').onClick(() => {const testData = { name: 'Tom', age: 18 };localStorage.put('indexValue', testData)this.renderMsg('add:' + JSON.stringify(testData))console.log('testTag add', testData)
})
重新获取本地存储值时,输出为Object,说明已转换成功。如下图:
四、添加时效性
有时某些数据需要在规定时间内才有效,这则需要对存储数据增加时效性的能力,即在存储数据过程中添加失效的时间,并在获取时判断是否在有效期内;这样所有保存的数据,将都需要转化为JSON对象格式的字符串进行存储。
首先,我们将之前LocalStorage.ets文件拷贝份,在之前基础上将其改造,增加时效性能力。如下图:
4.1 定义json数据存储类型
在修改存储方式前,需要定义json格式对象类型,在put()函数中存储前和getValue()函数中获取结果通过JSON.parse转换后的数据,都需要该类型进行约束。
// 定义json对象存储类型
type dataType = { value: valueType | object, expire: number }
4.2 修改put()函数
当put()方法中,添加失效时间expire时,则将其合并到JSON数据中一起存储;如果无expire失效时间,则传入-1,表示此数据长久有效,除非主动清除。
修改后代码如下:
/*** 定义增加函数* @param key* @param value* @param expire*/put(key: string, value: valueType | object, expire?: Date): void {// 定义存储Json格式对象const data = {value, // 存储内容expire : (expire ? expire.getTime() : -1) // 如果失效时间存在,将其转换为时间戳,否则传入-1}let dataStr: string;try {dataStr = JSON.stringify(data) // 当数据转换成功,将其存储} catch (e) {console.log('testTag error', e)return}this.preference.put(key, dataStr).then(() => this.preference.flush()).catch(e => {console.log('testTag error', e)})}
4.3 修改getValue()函数
在getValue()函数中,在获取数据时,除了需要将字符串数据转换为JSON格式对象外,还需判断其中expire字段,当前存储内容是否在有效期内;在有效期内则返回,不在则则返回空(null)。
修改后代码如下:
/*** 定义获取对应key数据* @param key*/async getValue(key: string): Promise<valueType | object> {// 首页判断key值是否存在,不存在返回空if(!this.preference.has(key)) {return Promise.resolve(null)}let value = (await this.preference.get(key, '')) as valueType// 判断如果为字符串类型数据,并且为JSON对象格式数据,将其转换为对象if('string' === typeof value && isJsonObject(value)) {try {const data: dataType = JSON.parse(value)// 如果当前存储内容无时效性,或者在时效期内,都直接返回if(data.expire === -1 || data.expire > Date.now()) {return Promise.resolve(data.value)}// 如果已失效,将其信息删除else {this.preference.delete(key)}} catch (e) {console.log('testTag error', e)return Promise.resolve(null) // 如果转换出错,返回null}} // 通过Promise异步回调将结果返回(如果内容不为JSON格式对象,或者过了时效期,返回null)return Promise.resolve(null)}
4.4 更改导入模块
需要注意的是,我们将需要applicationAbility.ets和index.ets中的导入模块进行修改,否则引入还是之前LocalStorage.ets中类,刚非新创建的LocalStorageObj.ets。
示例如下:
// 旧导入模块
import LocalStorage from '../utils/LocalStorage'// 引入新模块,替换掉旧的LocalStorage
import LocalStorage from '../utils/LocalStorageObj'
4.5 展示时效性
以上两个函数修改完成后,我们来执行下看看结果。打开pages/index.ets,在添加事件中,增加数据缓存的时效性。
首先传一个失效日期进去,看看获取的结果是什么。代码如下:
Button('添加').onClick(() => {const testData = { name: 'Tom', age: 18 };const expireDate = new Date()// 保存一个失效10分钟的日期expireDate.setMinutes(expireDate.getMinutes() - 10)// 存储数据localStorage.put('indexValue', testData, expireDate)this.renderMsg('add:' + JSON.stringify(testData))console.log('testTag add', testData)})Button('读取').onClick(() => {localStorage.getValue('indexValue').then(value => {this.renderMsg('get:' + value)console.log('testTag get', value)}).catch(err => {console.log('testTag error', err)})})
页面效果可见,添加了json数据后,获取结果为null;这是因为在添加时,将日期设置为失效10分钟了。如下图:
我们再将时间设置为有效后,再来看看结果。代码如下:
Button('添加').onClick(() => {const testData = { name: 'Tom', age: 18 };const expireDate = new Date()// 设置为24小时后失效expireDate.setHours(expireDate.getHours() + 24)// 存储数据localStorage.put('indexValue', testData, expireDate)this.renderMsg('add:' + JSON.stringify(testData))console.log('testTag add', testData)})Button('读取').onClick(() => {localStorage.getValue('indexValue').then(value => {this.renderMsg('get:' + (null !== value ? JSON.stringify(value) : value))console.log('testTag get', value)}).catch(err => {console.log('testTag error', err)})})
页面效果可见,在有效期内成功获取到了存储数据。如下图:
六、完整代码
下面则是该篇所有示例完整代码。
uitls/utils.ets文件代码如下:
/*** 判断字符串是否为JSON对象*/
export const isJsonObject = (value: string) : boolean => {try {const parseStr = JSON.parse(value)return 'object' === typeof parseStr && null !== parseStr} catch (e) {console.log('testTag', e)return false}
}
utils/LocalStorageObj.ets文件代码如下:
import common from '@ohos.app.ability.common'
import preferences from '@ohos.data.preferences'
import { isJsonObject } from './utils'// 定义存储值类型
type valueType = string | number | boolean
// 定义json对象存储类型
type dataType = { value: valueType | object, expire: number }/*** 定义LocalStorage类*/
export class LocalStorage {private preference: preferences.Preferences // 用户首选项实例对象// 定义初始化函数initial(context: common.UIAbilityContext): void {// 这里将UIAbility中应用上下文的moduleName作用为实例名称,即该项目的applicationpreferences.getPreferences(context, context.abilityInfo.moduleName).then(preference => {this.preference = preferenceconsole.log('testTag', 'success~')}).catch(e => {console.log('testTag error', e)})}/*** 定义增加函数* @param key* @param value* @param expire*/put(key: string, value: valueType | object, expire?: Date): void {// 定义存储Json格式对象const data : dataType = {value, // 存储内容expire : (expire ? expire.getTime() : -1) // 如果失效时间存在,将其转换为时间戳,否则传入-1}let dataStr: string = '';try {dataStr = JSON.stringify(data) // 当数据转换成功,将其存储console.log('testTag', dataStr)} catch (e) {console.log('testTag error', e)return}this.preference.put(key, dataStr).then(() => this.preference.flush()).catch(e => {console.log('testTag error', e)})}/*** 定义获取对应key数据* @param key*/async getValue(key: string): Promise<valueType | object> {// 首页判断key值是否存在,不存在返回空if(!this.preference.has(key)) {return Promise.resolve(null)}let value = (await this.preference.get(key, '')) as valueType// 判断如果为字符串类型数据,并且为JSON对象格式数据,将其转换为对象if('string' === typeof value && isJsonObject(value)) {try {const data: dataType = JSON.parse(value)console.log('testTag', data.expire, Date.now(), data.expire < Date.now())// 如果当前存储内容无时效性,或者在时效期内,都直接返回if(data.expire === -1 || data.expire > Date.now()) {return Promise.resolve(data.value)}// 如果已失效,将其信息删除else {this.preference.delete(key)}} catch (e) {console.log('testTag error', e)return Promise.resolve(null) // 如果转换出错,返回null}}// 通过Promise异步回调将结果返回(如果内容不为JSON格式对象,或者过了时效期,返回null)return Promise.resolve(null)}/*** 更新数据* @param key* @param value*/async update(key: string, value: valueType){try {const preValue = await this.getValue(key)if(preValue != value) {this.put(key, value)}} catch (e) {console.log('testTag error', e)}}/*** 定义移除函数* @param key*/remove(key: string): void {this.preference.delete(key).then(() => this.preference.flush()).catch(e => {console.log('testTag error', e)})}/*** 定义清除所有数据函数*/clearAll(): void {this.preference.clear().then(() => this.preference.flush()).catch(e => {console.log('testTag error', e)})}
}
/*** 实例LocalStorage*/
const localStorage = new LocalStorage()/*** 导出localStorage单例对象*/
export default localStorage as LocalStorage
pages/index.ets首页代码如下:
import localStorage from '../utils/LocalStorageObj'
let index = 0
@Entry
@Component
struct Index {@State message: string = ''// 重置内容renderMsg(message: string | number | boolean){this.message = message.toString()}build() {Row() {Column() {Row(){Text(this.message || '-').fontSize(30).fontWeight(FontWeight.Bold)}.width('100%').height('150vp')Row(){// 添加相关操作按钮Button('添加').onClick(() => {const testData = { name: 'Tom', age: 18 };const expireDate = new Date()// 设置为24小时后失效expireDate.setHours(expireDate.getHours() + 24)// 存储数据localStorage.put('indexValue', testData, expireDate)this.renderMsg('add:' + JSON.stringify(testData))console.log('testTag add', testData)})Button('读取').onClick(() => {localStorage.getValue('indexValue').then(value => {this.renderMsg('get:' + (null !== value ? JSON.stringify(value) : value))console.log('testTag get', value)}).catch(err => {console.log('testTag error', err)})})Button('删除').onClick(async () => {localStorage.remove('indexValue')const value = await localStorage.getValue('indexValue')this.renderMsg('delete:' + value)console.log('testTag delete', value)})Button('清空').onClick(() => {})}.width('100%').justifyContent(FlexAlign.Center)}.width('100%')}.height('100%').alignItems(VerticalAlign.Top)}
}
此篇先讲到这里,希望对大家有所帮助。
相关文章:

HarmonyOS开发 - 本地持久化之实现LocalStorage实例
用户首选项为应用提供Key-Value键值型的数据处理能力,支持应用持久化轻量级数据,并对其修改和查询。数据存储形式为键值对,键的类型为字符串型,值的存储数据类型包括数字型、字符型、布尔型以及这3种类型的数组类型。 说明&#x…...

【C++打怪之路Lv12】-- 模板进阶
#1024程序员节|征文# 🌈 个人主页:白子寰 🔥 分类专栏:重生之我在学Linux,C打怪之路,python从入门到精通,数据结构,C语言,C语言题集👈 希望得到您…...
第23周Java主流框架入门-SpringMVC 2.RESTful开发风格
课程笔记:RESTful 开发风格 课程介绍 本节课程介绍 RESTful 开发风格,以及如何在 Spring MVC 中应用这种开发模式。传统 MVC 开发通过 Servlet、JSP 和 Java Bean 实现前后端交互,而 RESTful 开发提供了一种新的理念,更适合现代…...

QT枚举类型转字符串和使用QDebug<<重载输出私有枚举类型
一 将QT自带的枚举类型转换为QString 需要的头文件: #include <QMetaObject> #include <QMetaEnum> 测试代码 const QMetaObject *metaObject &QImage::staticMetaObject;QMetaEnum metaEnum metaObject->enumerator(metaObject->indexOf…...

手机柔性屏全贴合视觉应用
在高科技日新月异的今天,手机柔性显示屏作为智能手机市场的新宠,以其独特的可弯曲、轻薄及高耐用性特性引领着行业潮流。然而,在利用贴合机加工这些先进显示屏的过程中,仍面临着诸多技术挑战。其中,高精度对位、应力控…...

《Python游戏编程入门》注-第3章3
《Python游戏编程入门》的“3.2.4 Mad Lib”中介绍了一个名为“Mad Lib”游戏的编写方法。 1 游戏玩法 “Mad Lib”游戏由玩家根据提示输入一些信息,例如男人姓名、女人姓名、喜欢的食物以及太空船的名字等。游戏根据玩家输入的信息编写出一个故事,如图…...

Netty-TCP服务端粘包、拆包问题(两种格式)
前言 最近公司搞了个小业务,需要使用TCP协议,我这边负责服务端。客户端是某个设备,客户端传参格式、包头包尾等都是固定的,不可改变,而且还有个蓝牙传感器,透传数据到这个设备,然后通过这个设备…...

centos安装指定版本的jenkins
打开jenkins镜像包官网,找到自己想要安装的版本,官网地址:https://mirrors.tuna.tsinghua.edu.cn/jenkins/redhat-stable 下载指定版本安装包: wget https://mirrors.tuna.tsinghua.edu.cn/jenkins/redhat-stable/jenkins-2.452.…...
QT 周期性的杀死一个进程(软件),一分钟后自动退出
1.原因:某软件开机自启动很烦,搞一个程序干掉这个自启动的软件 2.QT代码 main.cpp #include "KillXXX.h" #include <QtWidgets/QApplication>int main(int argc, char *argv[]) {QApplication a(argc, argv);KillXXX k;return a.exec…...

MySQL任意版本安装卸载和数据库原理图绘制
MYSQL任意版本安装和卸载 安装: 1、解压文件 --- 不能出现中文路径 2、在解压目录(安装目录)下: 1>.创建data文件夹 2>.创建配置文件my.txt 然后修改成ini格式 3、修改配置文件 basedirD:\\mysql\\mysql-5.7.28-winx64…...

技术成神之路:设计模式(二十三)解释器模式
相关文章:技术成神之路:二十三种设计模式(导航页) 介绍 解释器模式(Interpreter Pattern)是一种行为设计模式,用于定义一种语言的文法表示,并提供一个解释器来处理这种文法。它用于处理具有特定语法或表达…...

2024软考《软件设计师》-Python专题知识(含历年真题解析)
自2020年之后,软考软件设计师考试在综合知识部分开始增加Python编程语言相关考点,每年会考2~3分的样子。本文将结合近几年常考的内容,扩展一下Pyhton的基础知识!考前看一看,或许有所帮助。 一、基础语法 标识符 第一…...

基于大数据 Python+Vue 旅游推荐可视化系统(源码+LW+部署讲解+数据库+ppt)
!!!!!!!!! 会持续一直更新下去 有问必答 一键收藏关注不迷路 源码获取:https://pan.baidu.com/s/1aRpOv3f2sdtVYOogQjb8jg?pwdjf1d 提取码: jf1d &#…...
使用虚拟机搭建环境:CentOS7 Docker、MySQL、Redis 安装与配置
创作灵感 项目实践总结:记录了在虚拟机中安装与配置CentOS7环境下的Docker、MySQL、Redis的全过程,帮助理解和应用各项技术。技术笔记与问题总结:详细梳理了每一步安装的关键点与常见问题,并给出了解决方案。职业感悟与心得&…...

[分享] Docker容器可视化管理工具 - WGCLOUD
WGCLOUD是新一代运维监测平台,它可以监控Docker容器的各种性能数据,比如内存,cpu,Image,运行时间,运行状态,端口映射等信息 WGCLOUD也支持在页面启动,重启,停止Docker容…...
保存网页中 canvas 的内容
在开发人员工具中,保存网页中 canvas 的内容,可以用这个方法: 1. 在 dom 中创建一个下载按钮 <button id="save">保存</button>2. 控制台中运行: const gCanvas = document.querySelector(#page_1);function onSave() {gCanvas.toBlob((blob) =&g…...

PID控制原理
PID控制原理 PID控制器是一种经典且广泛应用于工业控制领域的反馈控制器,它由比例(P)、积分(I)和微分(D)三个部分组成。通过对这三个部分的综合调节,PID控制器能够实现对被控对象的…...
python 使用 企微机器人发送消息
import requestswecom_bot_webhook ""msg_text "" # 要发送的消息内容""" mentioned_mobile_list : 手机号列表 , 提醒手机号对应的群成员(某个成员) """ res requests.post(wecom_bot_webhook,json{"msgtype"…...
ARM/Linux嵌入式面经(五二):华为
文章目录 一面技术面相关问题1. **硬件改进的具体内容是什么?**硬件改进的具体内容深入询问及回答2. **在维护前任师兄的代码时,你遇到了哪些挑战?**问题回答面试官追问及回答3. **在嵌入式系统中,内存泄漏通常有哪些原因?**一、内存泄漏的主要原因二、内存泄漏的具体场景…...

[旧日谈]高清画面撕裂问题考
背景 无边框透明背景透明的窗口,在随着缩放比例非整数倍数放大时的画面发生了露底、撕裂问题。 当我们在使用Qt开发的时候,遇到了一个结构性问题。因为我们的软件是自己做的,所以要自己定义标题栏,所以我们设置了软件为FrameLess…...
[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?
🧠 智能合约中的数据是如何在区块链中保持一致的? 为什么所有区块链节点都能得出相同结果?合约调用这么复杂,状态真能保持一致吗?本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里…...

C++初阶-list的底层
目录 1.std::list实现的所有代码 2.list的简单介绍 2.1实现list的类 2.2_list_iterator的实现 2.2.1_list_iterator实现的原因和好处 2.2.2_list_iterator实现 2.3_list_node的实现 2.3.1. 避免递归的模板依赖 2.3.2. 内存布局一致性 2.3.3. 类型安全的替代方案 2.3.…...
进程地址空间(比特课总结)
一、进程地址空间 1. 环境变量 1 )⽤户级环境变量与系统级环境变量 全局属性:环境变量具有全局属性,会被⼦进程继承。例如当bash启动⼦进程时,环 境变量会⾃动传递给⼦进程。 本地变量限制:本地变量只在当前进程(ba…...

Mybatis逆向工程,动态创建实体类、条件扩展类、Mapper接口、Mapper.xml映射文件
今天呢,博主的学习进度也是步入了Java Mybatis 框架,目前正在逐步杨帆旗航。 那么接下来就给大家出一期有关 Mybatis 逆向工程的教学,希望能对大家有所帮助,也特别欢迎大家指点不足之处,小生很乐意接受正确的建议&…...
工程地质软件市场:发展现状、趋势与策略建议
一、引言 在工程建设领域,准确把握地质条件是确保项目顺利推进和安全运营的关键。工程地质软件作为处理、分析、模拟和展示工程地质数据的重要工具,正发挥着日益重要的作用。它凭借强大的数据处理能力、三维建模功能、空间分析工具和可视化展示手段&…...
将对透视变换后的图像使用Otsu进行阈值化,来分离黑色和白色像素。这句话中的Otsu是什么意思?
Otsu 是一种自动阈值化方法,用于将图像分割为前景和背景。它通过最小化图像的类内方差或等价地最大化类间方差来选择最佳阈值。这种方法特别适用于图像的二值化处理,能够自动确定一个阈值,将图像中的像素分为黑色和白色两类。 Otsu 方法的原…...

网络编程(UDP编程)
思维导图 UDP基础编程(单播) 1.流程图 服务器:短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…...
MySQL账号权限管理指南:安全创建账户与精细授权技巧
在MySQL数据库管理中,合理创建用户账号并分配精确权限是保障数据安全的核心环节。直接使用root账号进行所有操作不仅危险且难以审计操作行为。今天我们来全面解析MySQL账号创建与权限分配的专业方法。 一、为何需要创建独立账号? 最小权限原则…...

【JVM面试篇】高频八股汇总——类加载和类加载器
目录 1. 讲一下类加载过程? 2. Java创建对象的过程? 3. 对象的生命周期? 4. 类加载器有哪些? 5. 双亲委派模型的作用(好处)? 6. 讲一下类的加载和双亲委派原则? 7. 双亲委派模…...
6个月Python学习计划 Day 16 - 面向对象编程(OOP)基础
第三周 Day 3 🎯 今日目标 理解类(class)和对象(object)的关系学会定义类的属性、方法和构造函数(init)掌握对象的创建与使用初识封装、继承和多态的基本概念(预告) &a…...