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