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
})

        参数说明:

参数名类型是否必填描述
contextContext应用上下文。
namestringPreferences实例的名称。
callbackAsyncCallback<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: ValueTypePromise<ValueType>从缓存的Preferences实例中获取键对应的值,如果值为null或者非默认值类型,返回默认数据defValue
getAll()/Promise<Object>从缓存的Preferences实例中获取所有键值数据。
put()key: string, value: ValueTypePromise<void>将数据写入缓存的Preferences实例中,可通过flushflush将Preferences实例持久化
has()key: stringPromise<boolean>检查缓存的Preferences实例中是否包含名为给定Key的存储键值对。
delete()key: stringPromise<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类文件。如下图:

3304fdf3750349f880e4f4ddeeec1a56.png

2.1定义LocalStorage类

       定义LocalStorage类,需要先做好几下几步准备:

  1. 获取Preferences实例对象,需要传入UIAbility组件的应用上下文,可通过"@ohos.app.ability.common"模块获取UIAbility类型,并将其赋给context形参。
  2. 定义私有preference变量,用于接收获取到的Preferences实例,后期可以直接调用,无需重复获取。
  3. 定义存储值类型vueType,根据用户首选项描述已知,其只能为number、string和boolean三种类型。
  4. 最后将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()方法参数中。

9cae214e285243dd894eb452a0c2b7a4.png

        如果有朋友还在疑虑类中是否存在context问题,可以打开UIAblity模块,可以看到其类中定义了context变量,如下图:

92b2127f9dd74817bc02c602074213aa.png

        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)}
}

        页面效果如下图:

5310a0db46cc4a82b577e3e93cbbab4e.png

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实例保存到本地,页面呈现添加数据。如下图:

9c2d74776716494c8ad72aac8f2fd1ab.png

        有些朋友可能喜欢使用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)})
})

        将获取到的数据显示到界面中,则拿到的是上次缓存的数据。如下图:

9a32a9fee12241248e8a76861d93ceb3.png

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:
})

        先添加数据,然后执行删除事件,页面效果如下:

0d55fe63b4f04e99a5ed42773adf903f.pngb7ad53304c3248b58152a96994c7deba.png

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,说明已转换成功。如下图:

fd692a9135cc4fe18ec76d6f6e095b4e.pngd45e9abb9c174eb9803f4518df043f1a.png

四、添加时效性

        有时某些数据需要在规定时间内才有效,这则需要对存储数据增加时效性的能力,即在存储数据过程中添加失效的时间,并在获取时判断是否在有效期内;这样所有保存的数据,将都需要转化为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)}
}

        此篇先讲到这里,希望对大家有所帮助。

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

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

相关文章

java程序打包为一个exe程序

ok&#xff0c;最近学到了一个有意思的东西 那就是如何将自己写好的java程序打包成一个exe程序&#xff0c;发给别人&#xff0c;然后运行。 那么开始之前&#xff0c;请先安装整个工具&#xff1a; exe4j&#xff1a;https://www.ej-technologies.com/exe4j/download&#…

高并发设计模式之ForkJoin模式

分而治之是一种思想,所谓分而治之就是把一个复杂的算法问题按一定的分解方法分为规模较小的若干部分,然后逐个解决,分别找出各部分的解,最后把各部分的解在整合成整个问题的解.ForkJoin模式就是分而治之思想的另一种应用. ForkJoin模式的原理 ForkJoin模式先把一个大任务分解…

AMD XILINX 20nm器件价格上调25%

随着市场回暖&#xff0c;台积电也在调整价格策略&#xff0c;近期台积电上调了20nm的出厂价格。 据相关消息显示&#xff0c;AMD为了保障持续的供货和服务&#xff0c;也计划将20nm器件的价格统一上调25%&#xff0c;预计将于11月发布正式的涨价通知&#xff0c;并于2025年Q1开…

EfficientNet-B6模型实现ISIC皮肤镜图像数据集分类

项目源码获取方式见文章末尾&#xff01; 回复暗号&#xff1a;13&#xff0c;免费获取600多个深度学习项目资料&#xff0c;快来加入社群一起学习吧。 《------往期经典推荐------》 项目名称 1.【基于opencv答题卡识别判卷】 2.【卫星图像道路检测DeepLabV3Plus模型】 3.【G…

为何我们要将测试左移?回到过去的美好时光

以下为作者观点&#xff1a; 为何我们将测试左移&#xff1f;在传统的开发周期中&#xff0c;测试通常在功能完成后甚至在开发阶段结束时进行。左移测试通过从开发过程开始到整个开发过程整合测试活动来挑战这一点。 让我们首先讨论一下为什么我们选择“左移”&#xff0c;因…

java项目之基于智能推荐的卫生健康系统(springboot)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的基于智能推荐的卫生健康系统。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 项目简介&#xff1a; 基于智能推荐…

性能测试详解

&#x1f345; 点击文末小卡片 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 一、 性能测试术语解释 1. 响应时间 响应时间即从应用系统发出请求开始&#xff0c;到客户端接收到最后一个字节数据为止所消耗的时间。响应时间按软件的特点…

深度学习基础—循环神经网络(RNN)

引言 从本系列博客开始&#xff0c;我们将来一起学习一下NLP领域的相关基础知识&#xff0c;NLP领域重要的模型是RNN&#xff0c;在此之前&#xff0c;先来了解一些符号的含义。 1.符号定义 &#xff08;1&#xff09;符号定义 假设建立一个能够自动识别句中人名位置的序列模型…

【工具变量】自由贸易试验区试点DID数据集(2003-2023年)

数据简介&#xff1a;自由贸易试验区&#xff08;Free Trade Zone&#xff0c;简称FTZ&#xff09;是中国ZF在新形势下为了推进GG开放、提高开放型经济水平而采取的重要战略举措。自贸试验区在一国的部分领土内运入任何货物&#xff0c;被认为在关境以外&#xff0c;免于实施惯…

Flask

创建项目 Pycharm专业版 默认文件 Pycharm社区版没有自动创建这几个文件&#xff0c;手动创建即可。 运行 常规功能 debug模式 修改内容自动更新&#xff0c;否则需要重新启动运行项目才生效。 修改host 通网络内其他人可以通过我得ip访问该服务。 修改端口号 空格分隔…

[Wireshark] 使用Wireshark抓包https数据包并显示为明文、配置SSLKEYLOGFILE变量(附下载链接)

前言 wireshark安装包 链接&#xff1a;https://pan.quark.cn/s/febb28f57c01 提取码&#xff1a;fUCQ 链接失效&#xff08;可能会被官方和谐&#xff09;可评论或私信我重发 chrome与firefox在访问https网站的时候会将密钥写入这个环境变量SSLKEYLOGFILE中&#xff0c;在wir…

野火鲁班猫4 (RK3588)系统配置

烧写系统 参考文档 &#xff1a; https://doc.embedfire.com/linux/rk3588/quick_start/zh/latest/quick_start/apt/apt.html 先装第一个软件&#xff0c;然后打开第二个软件。点固件&#xff0c;选择Ubuntu最新的固件&#xff0c;这边目前是20240911这个。 我这边直接烧写到…

Servlet 3.0 新特性全解

文章目录 Servlet3.0新特性全解Servlet 3.0 新增特性Servlet3.0的注解Servlet3.0的Web模块支持servlet3.0提供的异步处理提供异步原因实现异步原理配置servlet类成为异步的servlet类具体实现异步监听器改进的ServletAPI(上传文件) Servlet3.0新特性全解 tomcat 7以上的版本都支…

[OceanBase-不止于记录]:揭秘双引擎战略,共探AI时代数据架构未来

前言 又到了一年一度大家最爱的探会文章&#xff0c;非常荣幸收到OceanBase官方的邀请参加2024 OceanBase 年度发布会&#xff0c;作为一个经常参加线下探会的博主&#xff0c;每一次体验都有所不同&#xff0c;每一次新技术的突破都让人感到无比兴奋。同时&#xff0c;作为数…

【论文复现】短期电力负荷

作者主页&#xff1a; 七七的个人主页 文章收录专栏&#xff1a; 论文复现 欢迎大家点赞 &#x1f44d; 收藏 ⭐ 加关注哦&#xff01;&#x1f496;&#x1f496; 短期电力负荷 论文发表问题背景一. 基本问题二. 本论文发现的问题 对于论文发现问题的解决方案&#xff1a;复现…

Java-I/O框架:FileReader类使用、FileWriter类的使用、字符流复制文件

视频链接&#xff1a;16.19 字符流复制文件_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1Tz4y1X7H7?spm_id_from333.788.videopod.episodes&vd_sourceb5775c3a4ea16a5306db9c7c1c1486b5&p19 1.FileReader类&#xff08;文件字符输出流&#xff09;使用 pac…

快速生成高质量提示词,Image to Prompt 更高效

抖知书老师推荐&#xff1a; 随着 AI 技术的不断发展&#xff0c;视觉信息与语言信息之间的转换变得越来越便捷。在如今的数字化生活中&#xff0c;图像与文字的交互需求愈发旺盛&#xff0c;很多人都希望能轻松将图像内容直接转化为文本描述。今天我们来推荐一款实用的 AI 工…

网安秋招面试

《Java代码审计》http://mp.weixin.qq.com/s?__bizMzkwNjY1Mzc0Nw&mid2247484219&idx1&sn73564e316a4c9794019f15dd6b3ba9f6&chksmc0e47a67f793f371e9f6a4fbc06e7929cb1480b7320fae34c32563307df3a28aca49d1a4addd&scene21#wechat_redirect 《Web安全》h…

Python小游戏14——雷霆战机

首先&#xff0c;你需要确保安装了Pygame库。如果你还没有安装&#xff0c;可以使用pip来安装&#xff1a; bash pip install pygame 代码如下&#xff1a; python import pygame import sys import random # 初始化Pygame pygame.init() # 设置屏幕大小 screen_width 800 scr…

编程之路:蓝桥杯备赛指南

文章目录 一、蓝桥杯的起源与发展二、比赛的目的与意义三、比赛内容与形式四、比赛前的准备五、获奖与激励六、蓝桥杯的影响力七、蓝桥杯比赛注意事项详解使用Dev-C的注意事项 一、蓝桥杯的起源与发展 蓝桥杯全国软件和信息技术专业人才大赛&#xff0c;简称蓝桥杯&#xff0c…