• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Persisting User Preferences (ArkTS)
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** to store lightweight KV 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. **Preferences** is not suitable for storing a large amount of data. It is ideal for storing personalized settings, such as font size and night mode switch.
7
8
9## Working Principles
10
11User applications call **Preference** through the ArkTS interface to read and write data files. You can load the persistence file data to a **Preferences** instance. Each file uniquely corresponds to an instance. The system stores each instance in memory through a static container until the instance is removed from the memory or the file is deleted.
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 a 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- Data cannot be encrypted for storage. If data encryption is required, encrypt the data first, and then store the ciphertext in **preferences** as a Uint8Array.
43
44### XML Constraints
45
46- 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.
47
48- The memory usage increases as more data is stored. The recommended data limit is 50 MB. For large datasets, using synchronous APIs to create a **Preferences** instance and persist data can be time-consuming. You are advised not to perform these operations in the main thread. Otherwise, app freeze issues may occur.
49
50### GSKV Constraints
51
52- GSKV does not support cross-platform operations. Before using it, call **isStorageTypeSupported** to check whether it is supported.
53- 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.
54
55
56
57## Available APIs
58
59The 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).
60
61| API                                                    | Description                                                        |
62| ------------------------------------------------------------ | ------------------------------------------------------------ |
63| getPreferencesSync(context: Context, options: Options): Preferences | Obtains a **Preferences** instance. This API returns the result synchronously.<br/> An asynchronous API is also provided.                   |
64| putSync(key: string, value: ValueType): void                 | Writes data to the **Preferences** instance. This API returns the result synchronously. An asynchronous API is also provided.|
65| hasSync(key: string): boolean                                | Checks whether the **Preferences** instance contains the KV pair of the given key. The value **true** means the instance contains the KV pair; the value **false** means the opposite. The key cannot be empty. This API returns the result synchronously.<br/> An asynchronous API is also provided.|
66| 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.|
67| 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.|
68| flush(callback: AsyncCallback&lt;void&gt;): void             | Flushes the data of this **Preferences** instance to a file for data persistence.|
69| 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.|
70| off(type: 'change', callback?: Callback&lt;string&gt;): void | Unsubscribes from data changes.                                          |
71| 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.|
72| isStorageTypeSupported(type: StorageType): boolean           | Checks whether the specified storage type is supported. The value **true** means that the storage type is supported, and **false** means the opposite.|
73
74
75## How to Develop
76
771. Import the **@kit.ArkData** module.
78
79   ```ts
80   import { preferences } from '@kit.ArkData';
81   ```
82
832. (Optional) Set the storage type.
84
85   This step is optional. By default, preferences data is stored in XML format. Since API version 18, GSKV is supported.
86
87   Before using GSKV, call **isStorageTypeSupported()** to check whether the current platform supports it.
88
89   If **false** is returned, the platform does not support GSKV. In this case, use XML.
90
91   ```ts
92    let isGskvSupported = preferences.isStorageTypeSupported(preferences.StorageType.GSKV);
93    console.info("Is gskv supported on this platform: " + isGskvSupported);
94   ```
95
963. Obtain a **Preferences** instance.
97
98   Obtain a **Preferences** instance in the default XML format.
99
100   <!--Del-->Stage model:<!--DelEnd-->
101
102   ```ts
103   import { UIAbility } from '@kit.AbilityKit';
104   import { BusinessError } from '@kit.BasicServicesKit';
105   import { window } from '@kit.ArkUI';
106
107   let dataPreferences: preferences.Preferences | null = null;
108
109   class EntryAbility extends UIAbility {
110     onWindowStageCreate(windowStage: window.WindowStage) {
111       let options: preferences.Options = { name: 'myStore' };
112       dataPreferences = preferences.getPreferencesSync(this.context, options);
113     }
114   }
115   ```
116
117   <!--Del-->FA model:
118
119   ```ts
120   // Obtain the context.
121   import { featureAbility } from '@kit.AbilityKit';
122   import { BusinessError } from '@kit.BasicServicesKit';
123
124   let context = featureAbility.getContext();
125   let options: preferences.Options =  { name: 'myStore' };
126   let dataPreferences: preferences.Preferences = preferences.getPreferencesSync(context, options);
127   ```
128   <!--DelEnd-->
129
130   Obtain a **Preferences** instance in GSKV format.
131
132   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.
133   <!--Del-->Stage model:<!--DelEnd-->
134
135   ```ts
136   import { UIAbility } from '@kit.AbilityKit';
137   import { BusinessError } from '@kit.BasicServicesKit';
138   import { window } from '@kit.ArkUI';
139
140   let dataPreferences: preferences.Preferences | null = null;
141
142   class EntryAbility extends UIAbility {
143     onWindowStageCreate(windowStage: window.WindowStage) {
144       let options: preferences.Options = { name: 'myStore', storageType: preferences.StorageType.GSKV };
145       dataPreferences = preferences.getPreferencesSync(this.context, options);
146     }
147   }
148   ```
149
150   <!--Del-->FA model:
151
152   ```ts
153   // Obtain the context.
154   import { featureAbility } from '@kit.AbilityKit';
155   import { BusinessError } from '@kit.BasicServicesKit';
156
157   let context = featureAbility.getContext();
158   let options: preferences.Options =  { name: 'myStore', storageType: preferences.StorageType.GSKV };
159   let dataPreferences: preferences.Preferences = preferences.getPreferencesSync(context, options);
160   ```
161   <!--DelEnd-->
162
163
1644. Write data.
165
166   Call **putSync()** to write data to a **Preferences** instance.
167
168   For the data stored in the default mode (XML), you can call **flush()** to persist the data written if required.
169
170   If GSKV is used, the data is persisted in a file on realtime basis after being written.
171
172   > **NOTE**
173   >
174   > If the key already exists, **putSync()** overwrites the value. You can use **hasSync()** to check whether the KV pair exists.
175
176   Example:
177
178   ```ts
179   import { util } from '@kit.ArkTS';
180   if (dataPreferences.hasSync('startup')) {
181     console.info("The key 'startup' is contained.");
182   } else {
183     console.info("The key 'startup' does not contain.");
184     // Add a KV pair.
185     dataPreferences.putSync('startup', 'auto');
186     // 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.
187     let uInt8Array1 = new util.TextEncoder().encodeInto("~! @#¥%......&* () --+? ");
188     dataPreferences.putSync('uInt8', uInt8Array1);
189   }
190   ```
191
1925. Read data.
193
194   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.
195
196   Example:
197
198   ```ts
199   let val = dataPreferences.getSync('startup', 'default');
200   console.info("The 'startup' value is " + val);
201   // If the value is a string containing special characters, it is stored in the Uint8Array format. Convert the obtained Uint8Array into a string.
202   let uInt8Array2 : preferences.ValueType = dataPreferences.getSync('uInt8', new Uint8Array(0));
203   let textDecoder = util.TextDecoder.create('utf-8');
204   val = textDecoder.decodeToString(uInt8Array2 as Uint8Array);
205   console.info("The 'uInt8' value is " + val);
206   ```
207
2086. Delete data.
209
210   Call **deleteSync()** to delete a KV pair.<br>Example:
211
212   ```ts
213   dataPreferences.deleteSync('startup');
214   ```
215
2167. Persist data.
217
218   You can use **flush()** to persist the data held in a **Preferences** instance to a file.
219
220   Example:
221
222   ```ts
223   dataPreferences.flush((err: BusinessError) => {
224     if (err) {
225       console.error(`Failed to flush. Code:${err.code}, message:${err.message}`);
226       return;
227     }
228     console.info('Succeeded in flushing.');
229   })
230   ```
231
2328. Subscribe to data changes.
233
234   Specify an observer as the callback to return the data changes for an application.
235
236   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.
237
238   Example:
239
240   ```ts
241   let observer = (key: string) => {
242     console.info('The key' + key + 'changed.');
243   }
244   dataPreferences.on('change', observer);
245   // The data is changed from 'auto' to 'manual'.
246   dataPreferences.put('startup', 'manual', (err: BusinessError) => {
247     if (err) {
248       console.error(`Failed to put the value of 'startup'. Code:${err.code},message:${err.message}`);
249       return;
250     }
251     console.info("Succeeded in putting the value of 'startup'.");
252     if (dataPreferences !== null) {
253       dataPreferences.flush((err: BusinessError) => {
254         if (err) {
255           console.error(`Failed to flush. Code:${err.code}, message:${err.message}`);
256           return;
257         }
258         console.info('Succeeded in flushing.');
259       })
260     }
261   })
262   ```
263
264   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()**).
265
266   Example:
267    ```ts
268    let observer = (key: string) => {
269      console.info('The key' + key + 'changed.');
270    }
271    dataPreferences.on('change', observer);
272    // The data is changed from 'auto' to 'manual'.
273    dataPreferences.put('startup', 'manual', (err: BusinessError) => {
274      if (err) {
275        console.error(`Failed to put the value of 'startup'. Code:${err.code},message:${err.message}`);
276        return;
277      }
278      console.info("Succeeded in putting the value of 'startup'.");
279    })
280    ```
281
2829. Delete a **Preferences** instance from the memory.
283
284   Call **deletePreferences()** to delete a **Preferences** instance from the memory. If the instance has a persistent file, the persistent file, backup file, and damaged file will also be deleted.
285
286   > **NOTE**
287   >
288   > - The deleted **Preferences** instance cannot be used for data operations. Otherwise, data inconsistency will be caused.
289   >
290   > - The deleted data and files cannot be restored.
291   >
292   > - If GSKV is used, this API cannot be called concurrently with other APIs (including multiple processes). Otherwise, unexpected behavior may occur.
293
294   Example:
295
296   ```ts
297   preferences.deletePreferences(this.context, options, (err: BusinessError) => {
298     if (err) {
299       console.error(`Failed to delete preferences. Code:${err.code}, message:${err.message}`);
300         return;
301     }
302     console.info('Succeeded in deleting preferences.');
303   })
304   ```
305<!--RP1--><!--RP1End-->
306