• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Persisting Preferences Data
2
3
4## When to Use
5
6The **Preferences** module provides APIs for processing data in the form of key-value (KV) pairs, including querying, modifying, and persisting KV pairs. You can use **Preferences** when you want a unique storage for global data. <br>The **Preferences** data is cached in the memory, which allows fast access when the data is required. If you want to persist data, you can use **flush()** to save the data to a file. The **Preferences** data occupies the application's memory space and cannot be encrypted through configuration. Therefore, it is recommended for storing personalized settings (font size and whether to enable the night mode) of applications.
7
8
9## Working Principles
10
11User applications call **Preference** through the ArkTS interface to read and write data files. You can load the data of a **Preferences** persistence file to a **Preferences** instance. Each file uniquely corresponds to an instance. The system stores the instance in memory through a static container until the instance is removed from the memory or the file is deleted. The following figure illustrates how **Preference** works.
12
13The preference persistent file of an application is stored in the application sandbox. You can use **context** to obtain the file path. For details, see [Obtaining Application File Paths](../application-models/application-context-stage.md#obtaining-application-file-paths).
14
15**Figure 1** Preferences working mechanism
16
17![preferences](figures/preferences.jpg)
18
19## Storage Types
20By default, user preferences are stored in XML format. Since API version 18, the GSKV format is provided.
21
22### XML
23Data is stored in the form of XML files, which allow high versatility and cross-platform operations. When XML is used, preference data operations are primarily performed in the memory. You can call **flush()** to persist the data when necessary. This storage type is recommended for single-process, small data volume scenarios.
24
25### GSKV
26GSKV is available since API version 18. It supports concurrent read and write in multiple processes. When GSKV is used, preference data operations are flushed to the storage device in real time. This storage type is recommended for multi-process concurrency scenarios.
27
28## Constraints
29
30### Preferences Constraints
31
32- The key in a KV pair must be a string and cannot be empty or exceed 1024 bytes.
33
34- If the value is of the string type, use the UTF-8 encoding format. It can be empty. If not empty, it cannot exceed 16 MB.
35
36- If the data to be stored contains a string that is not in UTF-8 format, store it in an Uint8Array. Otherwise, the persistent file may be damaged due to format errors.
37
38- After **removePreferencesFromCache** or **deletePreferences** is called, the subscription to data changes will be automatically canceled. After **getPreferences** is called again, you need to subscribe to data changes again.
39
40- Do not call **deletePreferences** concurrently with other APIs in multi-thread or multi-process mode. Otherwise, unexpected behavior may occur.
41
42### XML Constraints
43
44- The XML type (default for preferences) cannot ensure process concurrency safety, posing risks of file corruption and data loss. It is not recommended for use in multi-process scenarios.
45
46- Memory usage will increase with the amount of stored data. You are advised to keep the stored data below 50 MB. When the stored data is large, using synchronous APIs to create **Preferences** objects and persist data will become time consuming. Avoid using it in the main thread, as it may cause appfreeze problems.
47
48### GSKV Constraints
49
50- GSKV does not support cross-platform operations. Before using it, call **isStorageTypeSupported** to check whether it is supported.
51- In OpenHarmony, a user group is a logical collection of users with the same characteristics. These users share certain rights. User groups are used to facilitate system management and control user access rights. If the user group is involved when GSKV is used by multiple processes, ensure that the processes belong to the same group.
52
53
54
55## Available APIs
56
57The following table lists the APIs related to user preference persistence. For more information about the APIs, see [User Preferences](../reference/apis-arkdata/js-apis-data-preferences.md).
58
59| API                                                    | Description                                                        |
60| ------------------------------------------------------------ | ------------------------------------------------------------ |
61| getPreferencesSync(context: Context, options: Options): Preferences | Obtains a **Preferences** instance. This API returns the result synchronously.<br/> An asynchronous API is also provided.                   |
62| putSync(key: string, value: ValueType): void                 | Writes data to the **Preferences** instance. This API returns the result synchronously. An asynchronous API is also provided.|
63| hasSync(key: string): boolean                                | Checks whether the **Preferences** instance contains a KV pair with the given key. The key cannot be empty. This API returns the result synchronously.<br/> An asynchronous API is also provided.|
64| getSync(key: string, defValue: ValueType): ValueType         | Obtains the value of the specified key. If the value is null or not of the default value type, **defValue** is returned. This API returns the result synchronously.<br/> An asynchronous API is also provided.|
65| deleteSync(key: string): void                                | Deletes a KV pair from the **Preferences** instance. This API returns the result synchronously.<br/> An asynchronous API is also provided.|
66| flush(callback: AsyncCallback&lt;void&gt;): void             | Flushes the data of this **Preferences** instance to a file for data persistence.|
67| on(type: 'change', callback: Callback&lt;string&gt;): void   | Subscribes to data changes. A callback will be invoked after **flush()** is executed for the data changed.|
68| off(type: 'change', callback?: Callback&lt;string&gt;): void | Unsubscribes from data changes.                                          |
69| deletePreferences(context: Context, options: Options, callback: AsyncCallback&lt;void&gt;): void | Deletes a **Preferences** instance from memory. If the **Preferences** instance has a persistent file, this API also deletes the persistent file.|
70| isStorageTypeSupported(type: StorageType): boolean           | Checks whether the specified storage type is supported.|
71
72
73## How to Develop
74
751. Import the **@kit.ArkData** module.
76
77   ```ts
78   import { preferences } from '@kit.ArkData';
79   ```
80
812. (Optional) Set the storage type.
82
83   This step is optional. By default, preferences data is stored in XML format. Since API version 18, GSKV is supported.
84
85   Before using GSKV, call **isStorageTypeSupported()** to check whether the current platform supports it.
86
87   If **false** is returned, the platform does not support GSKV. In this case, use XML.
88
89   ```ts
90    let isGskvSupported = preferences.isStorageTypeSupported(preferences.StorageType.GSKV);
91    console.info("Is gskv supported on this platform: " + isGskvSupported);
92   ```
93
943. Obtain a **Preferences** instance.
95
96   Obtain a **Preferences** instance in the default XML format.
97
98   <!--Del-->Stage model:<!--DelEnd-->
99
100   ```ts
101   import { UIAbility } from '@kit.AbilityKit';
102   import { BusinessError } from '@kit.BasicServicesKit';
103   import { window } from '@kit.ArkUI';
104
105   let dataPreferences: preferences.Preferences | null = null;
106
107   class EntryAbility extends UIAbility {
108     onWindowStageCreate(windowStage: window.WindowStage) {
109       let options: preferences.Options = { name: 'myStore' };
110       dataPreferences = preferences.getPreferencesSync(this.context, options);
111     }
112   }
113   ```
114
115   <!--Del-->FA model:
116
117   ```ts
118   // Obtain the context.
119   import { featureAbility } from '@kit.AbilityKit';
120   import { BusinessError } from '@kit.BasicServicesKit';
121
122   let context = featureAbility.getContext();
123   let options: preferences.Options =  { name: 'myStore' };
124   let dataPreferences: preferences.Preferences = preferences.getPreferencesSync(context, options);
125   ```
126   <!--DelEnd-->
127
128   Obtain a **Preferences** instance in GSKV format.
129
130    If you want to use GSKV and the platform supports it, you can obtain the **Preferences** instance as follows. However, the storage type cannot be changed once selected.
131   <!--Del-->Stage model:<!--DelEnd-->
132
133   ```ts
134   import { UIAbility } from '@kit.AbilityKit';
135   import { BusinessError } from '@kit.BasicServicesKit';
136   import { window } from '@kit.ArkUI';
137
138   let dataPreferences: preferences.Preferences | null = null;
139
140   class EntryAbility extends UIAbility {
141     onWindowStageCreate(windowStage: window.WindowStage) {
142       let options: preferences.Options = { name: 'myStore' , storageType: preferences.StorageType.GSKV};
143       dataPreferences = preferences.getPreferencesSync(this.context, options);
144     }
145   }
146   ```
147
148   <!--Del-->FA model:
149
150   ```ts
151   // Obtain the context.
152   import { featureAbility } from '@kit.AbilityKit';
153   import { BusinessError } from '@kit.BasicServicesKit';
154
155   let context = featureAbility.getContext();
156   let options: preferences.Options =  { name: 'myStore' , storageType: preferences.StorageType.GSKV};
157   let dataPreferences: preferences.Preferences = preferences.getPreferencesSync(context, options);
158   ```
159   <!--DelEnd-->
160
161
1624. Write data.
163
164   Call **putSync()** to save data to the cached **Preferences** instance.
165
166   For the data stored in the default mode (XML), you can call **flush()** to persist the data written if required.
167
168   If GSKV is used, the data is persisted in a file on realtime basis after being written.
169
170   > **NOTE**
171   >
172   > If the key already exists, **putSync()** overwrites the value. You can use **hasSync()** to check whether the KV pair exists.
173
174   Example:
175
176   ```ts
177   import { util } from '@kit.ArkTS';
178   if (dataPreferences.hasSync('startup')) {
179     console.info("The key 'startup' is contained.");
180   } else {
181     console.info("The key 'startup' does not contain.");
182     // Add a KV pair.
183     dataPreferences.putSync('startup', 'auto');
184     // If a string contains special characters, convert the string to Uint8Array format and store it. The length of the string cannot exceed 16 x 1024 x 1024 bytes.
185     let uInt8Array1 = new util.TextEncoder().encodeInto("~! @#¥%......&* () --+? ");
186     dataPreferences.putSync('uInt8', uInt8Array1);
187   }
188   ```
189
1905. Read data.
191
192   Call **getSync()** to obtain the value of the specified key. If the value is null or is not of the default value type, the default data is returned.
193
194   Example:
195
196   ```ts
197   let val = dataPreferences.getSync('startup', 'default');
198   console.info("The 'startup' value is " + val);
199   // If the value is a string containing special characters, it is stored in the Uint8Array format. Convert the obtained Uint8Array into a string.
200   let uInt8Array2 : preferences.ValueType = dataPreferences.getSync('uInt8', new Uint8Array(0));
201   let textDecoder = util.TextDecoder.create('utf-8');
202   val = textDecoder.decodeToString(uInt8Array2 as Uint8Array);
203   console.info("The 'uInt8' value is " + val);
204   ```
205
2066. Delete data.
207
208   Call **deleteSync()** to delete a KV pair.<br>Example:
209
210   ```ts
211   dataPreferences.deleteSync('startup');
212   ```
213
2147. Persist data.
215
216   You can use **flush()** to persist the data held in a **Preferences** instance to a file. Example:
217
218   ```ts
219   dataPreferences.flush((err: BusinessError) => {
220     if (err) {
221       console.error(`Failed to flush. Code:${err.code}, message:${err.message}`);
222       return;
223     }
224     console.info('Succeeded in flushing.');
225   })
226   ```
227
2288. Subscribe to data changes.
229
230   Specify an observer as the callback to return the data changes for an application.
231
232   If the preferences data is stored in the default format (XML), the observer callback will be triggered only after the subscribed value changes and **flush()** is executed.
233
234   Example:
235
236   ```ts
237   let observer = (key: string) => {
238     console.info('The key' + key + 'changed.');
239   }
240   dataPreferences.on('change', observer);
241   // The data is changed from 'auto' to 'manual'.
242   dataPreferences.put('startup', 'manual', (err: BusinessError) => {
243     if (err) {
244       console.error(`Failed to put the value of 'startup'. Code:${err.code},message:${err.message}`);
245       return;
246     }
247     console.info("Succeeded in putting the value of 'startup'.");
248     if (dataPreferences !== null) {
249       dataPreferences.flush((err: BusinessError) => {
250         if (err) {
251           console.error(`Failed to flush. Code:${err.code}, message:${err.message}`);
252           return;
253         }
254         console.info('Succeeded in flushing.');
255       })
256     }
257   })
258   ```
259
260   If the preferences data is stored in GSKV format, the observer callback will be triggered after the subscribed value changes (without the need for calling **flush()**).
261
262   Example:
263    ```ts
264    let observer = (key: string) => {
265      console.info('The key' + key + 'changed.');
266    }
267    dataPreferences.on('change', observer);
268    // The data is changed from 'auto' to 'manual'.
269    dataPreferences.put('startup', 'manual', (err: BusinessError) => {
270      if (err) {
271        console.error(`Failed to put the value of 'startup'. Code:${err.code},message:${err.message}`);
272        return;
273      }
274      console.info("Succeeded in putting the value of 'startup'.");
275    })
276    ```
2779. Delete a **Preferences** instance from the memory.
278
279   Call **deletePreferences()** to delete a **Preferences** instance from the memory. If the **Preferences** instance has a persistent file, the persistent file and its backup and corrupted files will also be deleted.
280
281   > **NOTE**
282   >
283   > - The deleted **Preferences** instance cannot be used for data operations. Otherwise, data inconsistency will be caused.
284   >
285   > - The deleted data and files cannot be restored.
286   >
287   > - If GSKV is used, this API cannot be called concurrently with other APIs (including multiple processes). Otherwise, unexpected behavior may occur.
288
289   Example:
290
291   ```ts
292   preferences.deletePreferences(this.context, options, (err: BusinessError) => {
293     if (err) {
294       console.error(`Failed to delete preferences. Code:${err.code}, message:${err.message}`);
295         return;
296     }
297     console.info('Succeeded in deleting preferences.');
298   })
299   ```
300<!--RP1--><!--RP1End-->
301