This article introduces HongmengPreferences
, It is a lightweight storage method, where data is stored in memory and is used to store a small amount of data.
Can be executedflush()
Methods write data in memory to disk files to ensure that the data can continue to be used after the next restart. The following will be introduced
Main features:
- Data storage form: key-value pair, the type of key is a string, and the stored data types of values include numeric, character, Boolean and these three types of array typesClick to view
- Lightweight: Preferences are mainly used to store a small amount of data and are not suitable for storing large amounts of data sets. All data will be loaded into memory, too much data may lead to excessive memory usage
- Quick access: The read speed is very fast since the data is cached in memory
- Synchronous and asynchronous operations: provides two ways to handle data read and write operations.
Initialize Preferences instances
import { preferences } from '@'
import { BusinessError } from '@'
@Entry
@Component
struct Index {
dataPreferences: | null = null
aboutToAppear(): void {
let options: = { name: 'myStore' }
= (getContext(this), options)
}
}
-
(context: Context, options: Options)
Get Preferences instance -
Options: { name: string }
Specify the name of the Preferences instance
Methods for manipulating data (the following are asynchronous writing methods, and there will be synchronous writing methods below)
Write data put
Write data to cache
Sample code:
build() {
Column() {
Button('Login')
.onClick(async() => {
?.put('name', 'Strange Sex', (err: BusinessError) => {
if (err) {
(`Write data failed: code=${}, message=${}`)
Return
}
('Writing data successfully')
})
}
}
}
When we write data, if we do not need to pay attention to callback notifications, we can ignore them, as follows:
?.put('name', 'Strange Sex')
Get data get
Get the value corresponding to the key from the cache
get
The first parameter in the method is Key, and the second parameter is the default value. If the obtained data is null or non-default value type, the default value will be returned.
Sample code:
?.get('name', 'default', (err: BusinessError, val: ) => {
if (err) {
(`Failed to obtain data: code=${}, message=${}`)
Return
}
(`Successfully obtained data: val=${val}`)
})
Get all data getAll
Get all key-value data from cache
Sample code:
?.getAll((err: BusinessError, val: ) => {
('Get all data:', (val)) //Get all data: {"name1":"Weapon","name2":"Jess","name3":"Barrel"}
})
Check if there is
Check whether there is key value data for the specified key, the return value isboolean
Sample code:
?.has('name', (err: BusinessError, flag: boolean) => {
if (err) {
(`Check failed: code=${}, message=${}`)
Return
}
('Check whether the key value exists:', flag) // true
})
?.has('name222222', (err: BusinessError, flag: boolean) => {
if (err) {
(`Check failed: code=${}, message=${}`)
Return
}
('Check whether the key value exists:', flag) // false
})
Delete data delete
Delete the key value data of the specified key
Sample code:
?.delete('name', (err: BusinessError) => {
if (err) {
(`Deletion failed: code=${}, message=${}`)
Return
}
('Delete successfully')
})
Delete all data clear
Delete all data in the cache
Sample code:
?.clear((err: BusinessError) => {
if (err) {
(`Delete all data failed: code=${}, message=${}`)
}
('Delete all data successfully')
})
Synchronize cached data to disk file flush
effect: flush
Mainly used to synchronize memory changes to disk files. When data is manipulated (such as adding, updating, deleting), these changes are first saved in memory. Callflush
These changes are written to disk, ensuring that data remains available after the application restarts or the device restarts.
Sample code:
?.flush((err: BusinessError) => {
if (err) {
(`Failed to flush:code=${},message:${}}`)
return
}
('Succeeded in flush')
})
If the flush() method is not executed, the following effects may be:
- Data Loss Risk: If an application crashes or is forced to terminate before data is synchronized to a persistent storage, changes in memory may be lost. This means that the user's data may not be saved correctly.
- Data inconsistency: In some cases, if flush() is not called in time, it may cause inconsistency in data when read at different points in time. For example, one process may have updated the data but not synchronized yet, while another process is still reading the old data.
- Performance considerations: While the flush() method ensures data persistence, frequent calls to it may affect performance. Therefore, developers usually call flush() at the appropriate point in time (such as when the user explicitly saves the action or when the application is about to exit). Best Practices
To ensure data integrity and consistency, developers should follow the following best practices:
- After the key data is modified, the flush() method is called in time.
- Consider calling flush() in an application's lifecycle event (such as onPause() or onDestroy()) to ensure that the data is saved correctly before the application exits.
- Avoid frequent call to flush() in performance-sensitive operations, but select the appropriate time point for synchronization according to actual needs.
In short, although not calling the flush() method may not cause problems immediately in some cases, in order to ensure the reliability and durability of the data, developers should develop good habits to call the flush() method at appropriate times. .
Another asynchronous writing method
The above method example code is usedcallback asynchronous callbackIn addition to this method, there is another wayPromise asynchronous callback,as follows:
let promise = ?.get('name', 'default',)
promise?.then((val: ) => {
(`Successfully obtained data: val=${val}`)
}).catch((err: BusinessError) => {
(`Failed to obtain data: code=${}, message=${}`)
})
If you don't want to use itPromise
Chain callback, can also be usedasync/await:
let res = await ?.get('name', 'default',)
('Get data:', res)
Synchronous writing method
Except for the above methodflush
There is no synchronous writing method, and other methods have synchronous writing methods, such as:
- get
let value = dataPreferences?.getSync('name', 'default')
- put
let value = dataPreferences?.putSync('name', 'Xiao Luban')
Listen to data changes
Listen to all key data changes
Listen to all keys, and one of the key values changes, a callback will be triggered.(The callback callback will only be triggered after the flush method is executed)
grammar: on(type: 'change', callback: Callback<string>): void
Sample code:
@Entry
@Component
struct Index {
aboutToAppear(): void {
let observer = (key: string) => {
('The key that has changed data:', key) // The key that has changed data: name, magic ghost
}
('change', observer)
}
build() {
Column() {
Button('button')
.onClick(() => {
?.delete('name', (err: BusinessError) => {
if (err) {
(`Deletion failed: code=${}, message=${}`)
Return
}
('Delete successfully')
})
?.flush()
})
}
}
}
Listen to the data changes of the specified key
An array can be passed to specify a part of the key for listening
grammar: on(type: 'dataChange', keys: Array<string>, callback: Callback<Record<string, ValueType>>): void
Sample code:
let keys = ['name', 'age']
let observer = (data: Record<string, >) => {
for (const keyValue of (data)) {
(`observer : ${keyValue}`)
}
("The observer called.")
}
('dataChange', keys, observer);
Listen to data changes between processes
Listen to data changes between processes. When multiple processes hold the same preference file, the flush method is executed in any process (including this process). After the persistent file changes, the callback callback is triggered.
grammar: on(type: 'multiProcessChange', callback: Callback<string>): void
Sample code:
let observer = (key: string) => {
('The key that has changed data:', key)
}
?.on('multiProcessChange', observer)
Cancel monitoring
grammar: off(type: 'change', callback?: Callback<string>): void
If you need to cancel the specified callback function, you need to fill in the callback, and if you don't fill in it, you will cancel all of it.
Sample code: ('change', observer);
multiProcessChange
anddataChange
Similarly
Constraints used by Preferences
- Preferences cannot guarantee process concurrency security, there is a risk of file corruption and data loss, and are not supported for use in multi-process scenarios.
- Key key is of string type, and requires non-empty and no more than 1024 bytes in length.
- If the Value value is string type, please use UTF-8 encoding format, which can be empty, and the length does not exceed 16 * 1024 * 1024 bytes
- The memory will increase with the increase in the amount of stored data, so the amount of stored data should be lightweight. It is recommended that no more than 10,000 pieces of data be stored, otherwise it will cause greater overhead in memory.
Package
Make a simple package and play without going through too many tests
- Encapsulate a PreferencesUtil class
import { preferences } from '@'
import { BusinessError } from '@'
class PreferenceUtil {
private _preferences?:
private context?: Context
private fileName?: string
constructor(t?: Context, fileName: string = 'myStore') {
= t || getContext(this)
= fileName
()
}
init() {
if (this._preferences) {
this._preferences = (, { name: })
}
}
get(key: string): Promise<> {
return new Promise(resolve => {
this._preferences?.get(key, '', (err: BusinessError, val: ) => {
resolve(val)
})
})
}
getSync(key: string): Promise< | undefined> {
return new Promise(resolve => {
resolve(this._preferences?.getSync(key, ''))
})
}
put(key: string, value: ) {
this._preferences?.put(key, value, (err: BusinessError) => {
if (err) {
(`Write data failed: code=${}, message=${}`)
Return
}
('Writing data successfully')
()
})
}
putSync(key: string, value: ) {
this._preferences?.putSync(key, value)
()
}
delete(key: string) {
this._preferences?.delete(key, (err: BusinessError) => {
if (err) {
(`Deletion failed: code=${}, message=${}`)
Return
}
('Delete successfully')
()
})
}
deleteSync(key: string) {
this._preferences?.deleteSync(key)
()
}
clear() {
this._preferences?.clear((err: BusinessError) => {
if (err) {
(`Failed to clear all data: code=${}, message=${}`)
}
('Empty all data successfully')
()
})
}
clearSync() {
this._preferences?.clearSync()
()
}
has(key: string): Promise<boolean> {
return new Promise(resolve => {
this._preferences?.has(key, (err: BusinessError, flag: boolean) => {
if (err) {
(`Check failed: code=${}, message=${}`)
Return
}
resolve(flag)
('Check whether the key value exists:', flag) // true
})
})
}
hasSync(key: string): Promise<boolean | undefined> {
return new Promise(resolve => {
resolve(this._preferences?.hasSync(key))
})
}
flush() {
this._preferences?.flush((err: BusinessError) => {
if (err) {
(`Failed to flush:code=${}, message:${}}`)
Return
}
('Succeeded in flush')
})
}
}
export default PreferenceUtil
- Use within the component
import PreferenceUtil from './storage'
@Entry
@Component
struct Index {
preferences?: PreferencesUtil
async aboutToAppear(): Promise<void>{
= new PreferenceUtil()
('Get name', await ?.get('name'))
}
}
question
Data inconsistency problem
Click here to view
at last
If you don't understand, you can leave a message or read the document by yourselfDocument address