用户首选项为应用提供Key-Value键值型的数据处理能力,支持应用持久化轻量级数据,并对其修改和查询。数据存储形式为键值对,键的类型为字符串型,值的存储数据类型包括数字型、字符型、布尔型以及这3种类型的数组类型。
在上一篇中,对Preference实例进行封装,实现LocalStorage相关功能,并在基础上再增加对json对象数据存储能力,还实现了数据缓存具有时效性等功能。地址:HarmonyOS开发 - 本地持久化之实现LocalStorage实例-CSDN博客
不过LocalStorage示例为单例模式,当在多个Ability或多module情况下,不希望所有数据都存储在一个Preferences实例中,这就需要LocalStorage有支持多实例的能力。
一、Ability和Module
在HarmonyOS开发中,Ability是应用或服务所具备的能力的抽象,它可以响应用户的操作,与用户进行交互。一个Module可以包含一个或多个Ability。HarmonyOS提供了两种应用模式:FA(Feature Ability)模型和Stage模型。
FA模型从API7开始支持,但已不再是主推的模型。而Stage模型从API 9开始新增,是目前主推且会长期演进的模型。在Stage模型中,Ability分为两种组件类型:
- UIAbility组件:包含UI界面,提供展示UI的能力,主要用于和用户交互。
- ExtensionAbility组件:提供特定场景的扩展能力,满足更多的使用场景。当前仅OpenHarmony工程支持使用ExtensionAbility组件。
在HarmonyOS中,应用可以设计为包含多个Ability或者多个module,这取决于应用的复杂性和模块化需求。
1.1 多个Ability
一个应用可以包含多个Ability,每个Ability代表应用的一种能力。Ability分为Feature Ability(FA)和Particle Ability(PA)两种类型。FA是有UI界面的能力,用于与用户交互,例如一个页面或一个服务。PA是后台运行的能力,可以是Service Ability或Data Ability,用于提供后台任务或数据访问的能力。在Stage模型中,Ability进一步细分为UIAbility组件和ExtensionAbility组件,其中UIAbility组件包含UI界面,而ExtensionAbility组件提供特定场景的扩展能力。
多个Ability
1.2 多个module
在HarmonyOS中,应用可以被分解为多个功能模块,每个模块负责执行特定的功能。这些模块可以是Shared Library、Static Library或Visual Library。Shared Library是HSP(Harmony Shared Package)动态共享包,Static Library是HAR(Harmony Archive)静态共享包。模块化设计提高了代码的可理解性和可复用性,使应用的扩展和维护变得更为简便,同时降低了系统各部分之间的耦合度。
多个Module
1.3 二者选择
对于多应用的情况,你可以选择使用多个Ability或多个module,这取决于你的应用架构和业务需求。如果应用功能较为独立,且需要在不同应用间共享,那么可能更适合使用多个module。每个module可以包含一个或多个Ability,这样可以在不同的module之间实现功能的解耦和复用。如果应用的功能较为集中,且主要通过不同的页面或服务来提供用户交互,那么使用多个Ability可能更为合适。
在实际开发中,你可以根据应用的特点和需求,灵活选择使用多个Ability或多个module,或者将两者结合起来,以实现最佳的应用架构和性能。
二、LocalStorage升级
在前一篇(地址:HarmonyOS开发 - 本地持久化之实现LocalStorage实例-CSDN博客)中封装的LocalStorage类,创建Preferences实例时指定的实例名称用的是Module模块名称(context.abilityInfo.moduleName);在了解Ability和Module关系后,决定将实例名称更新为Ability名称(context.abilityInfo.name)。
2.1 LocalStorage类
这里将LocalStorageObj文件复制一份,在新文件中增加多实例功能,同时applicationability和Productability的Ability界面和pages页面中,引入的模块地址也需要同步修改为:"../utils/LocalStorageMulti"。
这里只需要在之前LocalStorage类基础上,增加一个静态变量用于存储键值对的实例,和一个创建实例的静态函数即可。
- 键值对的存储器(multiPreferences变量):利用Map对象的特性,键(key)为string类型,用于存储ability名称;值(value)为LocalStorge类型,用于存储类的实例对象。
- 创建实例函数(multiInstance()函数):通过Map对象中has()方法判断该对应的ability界面实例是否存在,不存在则创建并返回创建的LocalStorage实例;若存在则直接返回该键对应的LocalStorage实例。
示例代码如下:
TypeScript
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 // 用户首选项实例对象
// 定义多Preferences实例 存储变量字段
private static multiPreferences: Map<string, LocalStorage> = new Map()
/**
* 创建多实例
* @param context
*/
static multiInstance(context?: common.UIAbilityContext): LocalStorage {
const name = context.abilityInfo.name
// 如果存在该实例,则直接返回
if(LocalStorage.multiPreferences.has(name)) {
console.log('testTag context.abilityInfo.name update', name)
return LocalStorage.multiPreferences.get(name)
}
// 如果不存在,则创建实例
else {
console.log('testTag context.abilityInfo.name create', name)
const instance = new LocalStorage() // 实例LocalStorage对象
instance.initial(context) // 初始化Preferences实例
// 存储LocalStorage对象
LocalStorage.multiPreferences.set(name, instance)
// 返回实例对象
return instance
}
}
// 定义初始化函数
initial(context: common.UIAbilityContext): void {
// 这里将UIAbility中应用上下文的name作用为实例名称,即该项目的ApplicationAbility或ProductAbility
preferences.getPreferences(context, context.abilityInfo.name).then(preference => {
this.preference = preference
console.log('testTag', 'success~')
}).catch(e => {
console.log('testTag error', e)
})
}
// 略...
}
注意:initial()函数中须将之前context.abilityinfo.moduleName修改为context.abilityinfo.name。
2.2 Ability中初始化实例
在两个Ability中,引入LocalStorage模块,并初始化实例。由于multiinstance()为静态函数,通过LocalStorage类直接调用即可。
applicationability组件:
Productability组件:
2.3 page页面中获取实例
在HarmonyOS中,aboutToAppear是页面生命周期中的一个关键回调,它在创建自定义组件的新实例后,在执行其build()函数之前执行。这个回调允许开发者在组件实例创建后改变状态变量,并这些更改将在后续执行build()函数中生效。
aboutToAppear的使用场景包括但不限于:
- 初始化页面状态变量
- 执行数据绑定和事件监听的设置
- 执行与页面显示相关的动画或过渡效果
总的来说,aboutToAppear是HarmonyOS中管理页面生命周期的一个重要工具,它提供了页面显示前执行初始化操作的能力。而且多应用环境中,合理地使用多个Ability或module可以有效地组织和管理复杂的应用结构。
因此,我们将通过aboutToAppear来获取本Ability界面中的LocalStorage实例对象。
applicationability界面下的pages/index.ets示例代码如下:
TypeScript
import { LocalStorage } from '../utils/LocalStorageMulti'
import common from '@ohos.app.ability.common'
import Want from '@ohos.app.ability.Want'
@Entry
@Component
struct Index {
@State message: string = ''
private localStorage: LocalStorage // 定义变量,接收本Ability界面下的LocalStorage实例对象
aboutToAppear(){
// 获取当前上下文的LocalStorage实例对象
this.localStorage = LocalStorage.multiInstance(getContext(this) as common.UIAbilityContext)
}
// 重置内容
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)
// 存储数据
this.localStorage.put('indexValue', testData, expireDate)
this.renderMsg('add:' + JSON.stringify(testData))
console.log('testTag add', testData)
})
Button('读取').onClick(() => {
this.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 () => {
this.localStorage.remove('indexValue')
const value = await this.localStorage.getValue('indexValue')
this.renderMsg('delete:' + value)
console.log('testTag delete', value)
})
Button('跳转新页面').onClick(() => {
const context = getContext(this) as common.UIAbilityContext
const want: Want = {
bundleName: 'com.example.myapplication',
abilityName: 'ProductAbility',
moduleName: 'application',
parameters: {
instanceKey: 'product'
}
}
context.startAbility(want)
})
}.width('100%').justifyContent(FlexAlign.Center)
}
.width('100%')
}
.height('100%').alignItems(VerticalAlign.Top)
}
}
对LocalStorage类进行多实例改造后,数据的存储能力依然能正常执行。如下图:
Productability界面下的pages/indexProduct.ets示例代码如下:
TypeScript
import { LocalStorage } from '../utils/LocalStorageMulti'
import common from '@ohos.app.ability.common'
let index = 0
@Entry
@Component
struct Index {
@State message: string = ''
private localStorage: LocalStorage // 定义变量,接收本Ability界面下的LocalStorage实例对象
aboutToAppear(){
// 获取当前上下文的LocalStorage实例对象
this.localStorage = LocalStorage.multiInstance(getContext(this) as common.UIAbilityContext)
}
// 重置内容
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(() => {
this.localStorage.put('indexValue', ++index)
this.renderMsg('add:' + index)
console.log('testTag add', index)
})
Button('读取').onClick(() => {
this.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 () => {
this.localStorage.remove('indexValue')
const value = await this.localStorage.getValue('indexValue')
this.renderMsg('delete:' + value)
console.log('testTag delete', value)
})
}.width('100%').justifyContent(FlexAlign.Center)
}
.width('100%')
}
.height('100%').alignItems(VerticalAlign.Top)
}
}
在Applicationability界面点击添加和读取后,再跳转到Productability界面,点击"读取"按钮,看是否能获取到Applicationability界面的indexValue键对应的值。如下图,显示是获取不到的,虽然两则都存的键为indexValue,但都存储在不能的Preferences实例中。
在Productability界面,点击添加后,再读取。结果如下图:
2.4 multiInstance()函数
在LocalStorage实现多实例功能时,对multiInstance()函数的设计,是可以无须先初始化实例,直接在页面中aboutToAppear回调函数并获取当前Ability界面实例。
此时我们将两个Ability中的onCreate()函数,执行的LocalStorage.multiInstance()删除掉,只在page页面直接获取,再看下结果。
如上图,初始化实例调用的函数注释掉后,结果是怎样的。
如上图,此时只执行了页面中的multiInstance()函数,但操作结果依然是正常的;不过注意的是,在aboutToAppear回调函数中只是初始化LocalStorage实例是没问题的,但是直接调用实例对象的put()、get()、remove()、clear()等函数,可能会报错,显示preference实例不在存,这是因为initial()函数中是通过Promise异步回调获取的preference实例的。
所以,是否需要在ability中执行初始化,还是在页面中执行初始化,根据实现需求而定。
2.5 总结
在多应用的环境中,你可能会使用多个Ability或多个module来组织你的应用。Ability允许你创建多个具有不同功能的页面或服务,而module则允许你将应用分解为多个功能模块,每个模块可以包含一个或多个Ability。这样的设计有助于提高代码的可维护性和可扩展性。
该篇是为上一篇(HarmonyOS开发 - 本地持久化之实现LocalStorage实例-CSDN博客)的补充内容,希望对大家有所帮助。