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 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<void>): void | Flushes the data of this **Preferences** instance to a file for data persistence.| 67| on(type: 'change', callback: Callback<string>): void | Subscribes to data changes. A callback will be invoked after **flush()** is executed for the data changed.| 68| off(type: 'change', callback?: Callback<string>): void | Unsubscribes from data changes. | 69| deletePreferences(context: Context, options: Options, callback: AsyncCallback<void>): 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