提交 05ab0966 编写于 作者: A Annie_wang

update docs

Signed-off-by: NAnnie_wang <annie.wangli@huawei.com>
上级 9e325ef8
# Data Management
- Distributed Data Service
- [Distributed Data Service Overview](database-mdds-overview.md)
- [Distributed Data Service Development](database-mdds-guidelines.md)
- Relational Database
- [RDB Overview](database-relational-overview.md)
- [RDB Development](database-relational-guidelines.md)
- Preferences
- [Preferences Overview](database-preference-overview.md)
- [Preferences Development](database-preference-guidelines.md)
- Distributed Data Object
- [Distributed Data Object Overview](database-distributedobject-overview.md)
- [Distributed Data Object Development](database-distributedobject-guidelines.md)
- Data Share
- [DataShare Overview](database-datashare-overview.md)
- [DataShare Development](database-datashare-guidelines.md)
- [Data Management Overview](data-mgmt-overview.md)
- Application Data Persistence
- [Overview of Application Data Persistence](app-data-persistence-overview.md)
- [Persisting Preferences Data](data-persistence-by-preferences.md)
- [Persisting KV Store Data](data-persistence-by-kv-store.md)
- [Persisting RDB Store Data] (data-persistence-by-rdb-store.md)
- Distributed Application Data Synchronization
- [Distributed Application Data Synchronization Overview](sync-app-data-across-devices-overview.md)
- [Cross-Device Synchronization of KV Stores](data-sync-of-kv-store.md)
- [Cross-Device Synchronization of RDB Stores](data-sync-of-rdb-store.md)
- [Cross-Device Synchronization of Distributed Data Objects](data-sync-of-distributed-data-object.md)
- Data Reliability and Security
- [Data Reliability and Security Overview](data-reliability-security-overview.md)
- [Database Backup and Restoration](data-backup-and-restore.md)
- [Database Encryption](data-encryption.md)
- [Access Control by Device and Data Level](access-control-by-device-and-data-level.md)
- Cross-Application Data Sharing (Available Only for System Applications)
- [Cross-Application Data Sharing Overview](share-device-data-across-apps-overview.md)
- [Sharing Data Using DataShareExtensionAbility](share-data-by-datashareextensionability.md)
- [Sharing Data in Silent Access](share-data-by-silent-access.md)
# Access Control by Device and Data Level
## Basic Concepts
Distributed data management implements access control based on data security labels and device security levels.
A higher data security label and device security level indicate stricter encryption and access control measures and higher data security.
### Data Security Labels
The data can be rated into four security levels: S1, S2, S3, and S4.
| Risk Level| Security Level| Definition| Example|
| -------- | -------- | -------- | -------- |
| Critical| S4 | Special data types defined by industry laws and regulations, involving the most private individual information or data that may cause significant adverse impact on an individual or group once disclosed, tampered with, corrupted, or destroyed.| Political opinions, religious and philosophical belief, trade union membership, genetic data, biological information, health and sexual life status, sexual orientation, device authentication, and personal credit card information|
| High| S3 | Data that may cause critical adverse impact on an individual or group once disclosed, tampered with, corrupted, or destroyed.| Individual real-time precise positioning information and movement trajectory|
| Moderate| S2 | Data that may cause major adverse impact on an individual or group once disclosed, tampered with, corrupted, or destroyed.| Detailed addresses and nicknames of individuals|
| Low| S1 | Data that may cause minor adverse impact on an individual or group once disclosed, tampered with, corrupted, or destroyed.| Gender, nationality, and user application records|
### Device Security Levels
Device security levels are classified into SL1 to SL5 based on devices' security capabilities, for example, whether a Trusted Execution Environment (TEE) or a secure storage chip is available. For example, the development boards RK3568 and Hi3516 are SL1 (lower security) devices, and tablets are SL4 (higher security) devices.
During device networking, you can run the **hidumper -s 3511** command to query the device security level. The following example shows how to query the security level of the RK3568 board:
![en-us_image_0000001542496993](figures/en-us_image_0000001542496993.png)
## Access Control Mechanism in Cross-Device Synchronization
In cross-device data synchronization, data access is controlled based on the device security level and data security labels. In principle, data can be synchronized only to the devices whose data security labels are not higher than the device's security level. The access control matrix is as follows:
|Device Security Level|Data Security Labels of the Synchornizable Device|
|---|---|
|SL1|S1|
|SL2|S1 to S2|
|SL3|S1 to S3|
|SL4|S1 to S4|
|SL5|S1 to S4|
For example, the security level of development boards RK3568 and Hi3516 is SL1. The database with data security label S1 can be synchronized with RK3568 and Hi3516, but the databases with database labels S2-S4 cannot.
## When to Use
The access control mechanism ensures secure data storage and synchronization across devices. When creating a database, you need to correctly set the security level for the database.
## Setting the Security Level for a KV Store
When a KV store is created, the **securityLevel** parameter specifies the security level of the KV store. The following example shows how to create a KV store with security level of S1.
For details about the APIs, see [Distributed KV Store](../reference/apis/js-apis-distributedKVStore.md).
```js
import distributedKVStore from '@ohos.data.distributedKVStore';
let kvManager;
let context = getContext(this);
const kvManagerConfig = {
context: context,
bundleName: 'com.example.datamanagertest'
}
try {
kvManager = distributedKVStore.createKVManager(kvManagerConfig);
console.info('Succeeded in creating KVManager.');
} catch (e) {
console.error(`Failed to create KVManager. Code:${e.code},message:${e.message}`);
}
let kvStore;
try {
const options = {
createIfMissing: true,
encrypt: true,
backup: false,
autoSync: true,
kvStoreType: distributedKVStore.KVStoreType.SINGLE_VERSION,
securityLevel: distributedKVStore.SecurityLevel.S1
};
kvManager.getKVStore('storeId', options, (err, store) => {
if (err) {
console.error(`Failed to get KVStore. Code:${err.code},message:${err.message}`);
return;
}
console.info('Succeeded in getting KVStore.');
kvStore = store;
});
} catch (e) {
console.error(`An unexpected error occurred. Code:${e.code},message:${e.message}`);
}
```
## Setting the Security Level for an RDB Store
When an RDB store is created, the **securityLevel** parameter specifies the security level of the RDB store. The following example shows how to create an RDB store with security level of S1.
For details about the APIs, see [RDB Store](../reference/apis/js-apis-data-relationalStore.md).
```js
import relationalStore from '@ohos.data.relationalStore';
let store;
const STORE_CONFIG = {
name: 'RdbTest.db',
securityLevel: relationalStore.SecurityLevel.S1
};
let promise = relationalStore.getRdbStore(this.context, STORE_CONFIG);
promise.then(async (rdbStore) => {
store = rdbStore;
console.info('Succeeded in getting RdbStore.')
}).catch((err) => {
console.error(`Failed to get RdbStore. Code:${err.code},message:${err.message}`);
})
```
# Application Data Persistence Overview
Application data persistence means to save the application data in the memory to a file or database on a device. The data in the memory is usually saved in the forms of data structs or data objects, and the data in storage media can be saved in the forms of text, databases, or binary files.
The OpenHarmony standard system supports typical data storage forms, including user preferences (**Preferences**), key-value databases (**KV-Store**), and relational databases (**RelationalStore**).
You can use proper data storage forms to implement data persistence:
- **Preferences**: used to store application configuration data. Data is stored as text files on a device. When the application is used, it loads all the data from the text file to the memory. **Preferences** allow fast and efficient data access, but are not suitable when a large amount of data needs to be stored.
- **KV-Store**: used to store data in KV pairs, in which the key uniquely identifies the data. A KV store is a kind of non-relational database. It is ideal for storing service data with few data and service relationships. It has been widely used because it poses fewer database version compatibility issues in distributed scenarios and simplifies conflict handling in data synchronization. KV databases feature higher cross-device and cross-version compatibility than relational databases.
- **RelationalStore**: used to store data in rows and columns. It is widely used to process relational data in applications. RelationalStore provides a set of APIs for adding, deleting, modifying, and querying data. You can also define and use SQL statements for complex service scenarios.
# Database Backup and Restoration
## When to Use
You may need to restore a database in any of the following cases:
An important operation being performed by an application is interrupted.
The database is unavailable due to data loss or corruption, or dirty data.
Both KV stores and RDB stores support database backup and restoration. In addition, KV stores allow you to delete database backups to release local storage space.
## Backing Up, Restoring, and Deleting a KV Store
You can use **backup()** to back up a KV store, use **restore()** to restore a KV store, and use **deletebackup()** to delete a KV store backup file. For details about the APIs, see [Distributed KV Store](../reference/apis/js-apis-distributedKVStore.md).
1. Create a KV store.
(1) Create a **kvManager** instance.
(2) Set database parameters.
(3) Create a **kvStore** instance.
```js
import distributedKVStore from '@ohos.data.distributedKVStore';
let kvManager;
let context = getContext(this);
const kvManagerConfig = {
context: context,
bundleName: 'com.example.datamanagertest'
}
try {
kvManager = distributedKVStore.createKVManager(kvManagerConfig);
console.info('Succeeded in creating KVManager.');
} catch (e) {
console.error(`Failed to create KVManager. Code:${e.code},message:${e.message}`);
}
let kvStore;
try {
const options = {
createIfMissing: true,
encrypt: false,
backup: false,
autoSync: true,
kvStoreType: distributedKVStore.KVStoreType.SINGLE_VERSION,
securityLevel: distributedKVStore.SecurityLevel.S2
};
kvManager.getKVStore('storeId', options, (err, store) => {
if (err) {
console.error(`Fail to get KVStore. Code:${err.code},message:${err.message}`);
return;
}
console.info('Succeeded in getting KVStore.');
kvStore = store;
});
} catch (e) {
console.error(`An unexpected error occurred. Code:${e.code},message:${e.message}`);
}
```
2. Use **put()** to insert data to the KV store.
```js
const KEY_TEST_STRING_ELEMENT = 'key_test_string';
const VALUE_TEST_STRING_ELEMENT = 'value_test_string';
try {
kvStore.put(KEY_TEST_STRING_ELEMENT, VALUE_TEST_STRING_ELEMENT, (err) => {
if (err !== undefined) {
console.error(`Fail to put data. Code:${err.code},message:${err.message}`);
return;
}
console.info('Succeeded in putting data.');
});
} catch (e) {
console.error(`An unexpected error occurred. Code:${e.code},message:${e.message}`);
}
```
3. Use **backup()** to back up the KV store.
```js
let file = 'BK001';
try {
kvStore.backup(file, (err) => {
if (err) {
console.error(`Fail to backup data.code:${err.code},message:${err.message}`);
} else {
console.info('Succeeded in backupping data.');
}
});
} catch (e) {
console.error(`An unexpected error occurred. Code:${e.code},message:${e.message}`);
}
```
4. Use **delete()** to delete data to simulate unexpected deletion or data tampering.
```js
try {
kvStore.delete(KEY_TEST_STRING_ELEMENT, (err) => {
if (err !== undefined) {
console.error(`Fail to delete data. Code:${err.code},message:${err.message}`);
return;
}
console.info('Succeeded in deleting data.');
});
} catch (e) {
console.error(`An unexpected error occurred. Code:${e.code},message:${e.message}`);
}
```
5. Use **restore()** to restore the KV store.
```js
let file = 'BK001';
try {
kvStore.restore(file, (err) => {
if (err) {
console.error(`Fail to restore data. Code:${err.code},message:${err.message}`);
} else {
console.info('Succeeded in restoring data.');
}
});
} catch (e) {
console.error(`An unexpected error occurred. Code:${e.code},message:${e.message}`);
}
```
6. Use **deleteBackup()** to delete the backup file to release storage space.
```js
let kvStore;
let files = ['BK001'];
try {
kvStore.deleteBackup(files).then((data) => {
console.info(`Succeed in deleting Backup. Data:filename is ${data[0]},result is ${data[1]}.`);
}).catch((err) => {
console.error(`Fail to delete Backup. Code:${err.code},message:${err.message}`);
})
} catch (e) {
console.error(`An unexpected error occurred. Code:${e.code},message:${e.message}`);
}
```
## Backing Up and Restoring an RDB Store
You can use **backup()** to back up an RDB store, and use **restore()** to restore an RDB store. For details about the APIs, see [RDB Store](../reference/apis/js-apis-data-relationalStore.md).
1. Use **getRdbStore()** to create an RDB store.
```js
import relationalStore from '@ohos.data.relationalStore';
let store;
let context = getContext(this);
const STORE_CONFIG = {
name: 'RdbTest.db',
securityLevel: relationalStore.SecurityLevel.S1
};
relationalStore.getRdbStore(context, STORE_CONFIG, (err, rdbStore) => {
store = rdbStore;
if (err) {
console.error(`Failed to get RdbStore. Code:${err.code},message:${err.message}`);
return;
}
store.executeSql("CREATE TABLE IF NOT EXISTS EMPLOYEE (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, age INTEGER, salary INTEGER, codes Uint8Array);", null);
console.info('Succeeded in getting RdbStore.');
})
```
2. Use **insert()** to insert data to the RDB store.
```js
const valueBucket = {
'NAME': 'Lisa',
'AGE': 18,
'SALARY': 100.5,
'CODES': new Uint8Array([1, 2, 3, 4, 5])
};
store.insert('EMPLOYEE', valueBucket, relationalStore.ConflictResolution.ON_CONFLICT_REPLACE, (err, rowId) => {
if (err) {
console.error(`Failed to insert data. Code:${err.code},message:${err.message}`);
return;
}
console.info(`Succeeded in inserting data. rowId:${rowId}`);
})
```
3. Use **backup()** to back up the RDB store.
```js
store.backup('dbBackup.db', (err) => {
if (err) {
console.error(`Failed to backup data. Code:${err.code},message:${err.message}`);
return;
}
console.info(`Succeeded in backuping data.`);
})
```
4. Use **delete()** to delete data to simulate unexpected deletion or data tampering.
```js
let predicates = new relationalStore.RdbPredicates('EMPLOYEE');
predicates.equalTo('NAME', 'Lisa');
let promise = store.delete(predicates);
promise.then((rows) => {
console.info(`Delete rows: ${rows}`);
}).catch((err) => {
console.error(`Failed to delete data. Code:${err.code},message:${err.message}`);
})
```
5. Use **restore()** to restore the RDB store.
```js
store.restore('dbBackup.db', (err) => {
if (err) {
console.error(`Failed to restore data. Code:${err.code},message:${err.message}`);
return;
}
console.info(`Succeeded in restoring data.`);
})
```
# Database Encryption
## When to Use
OpenHarmony provides the database encryption capability to effectively protect the data stored in a database. Database encryption allows data to be stored and used in ciphertext, ensuring data confidentiality and integrity.
The encrypted database can be accessed only using an API, and the database file cannot be opened in other ways. Whether a database is encrypted is set when the database is created, and the setting cannot be changed.
Both KV stores and RDB stores support database encryption.
## Encrypting a KV Store
When a KV store is created, the **encrypt** parameter in **options** specifies whether to encrypt the KV store. The value **true** means to encrypt the KV store, and the value **false** (default) means the opposite.
For details about the APIs, see [Distributed KV Store](../reference/apis/js-apis-distributedKVStore.md).
```js
import distributedKVStore from '@ohos.data.distributedKVStore';
let kvManager;
let context = getContext(this);
const kvManagerConfig = {
context: context,
bundleName: 'com.example.datamanagertest',
}
try {
kvManager = distributedKVStore.createKVManager(kvManagerConfig);
console.info('Succeeded in creating KVManager.');
} catch (e) {
console.error(`Failed to create KVManager. Code:${e.code},message:${e.message}`);
}
let kvStore;
try {
const options = {
createIfMissing: true,
// Whether to encrypt the KV store.
encrypt: true,
backup: false,
autoSync: true,
kvStoreType: distributedKVStore.KVStoreType.SINGLE_VERSION,
securityLevel: distributedKVStore.SecurityLevel.S2
};
kvManager.getKVStore('storeId', options, (err, store) => {
if (err) {
console.error(`Fail to get KVStore. Code:${err.code},message:${err.message}`);
return;
}
console.info('Succeeded in getting KVStore.');
kvStore = store;
});
} catch (e) {
console.error(`An unexpected error occurred. Code:${e.code},message:${e.message}`);
}
```
## Encrypting an RDB Store
When an RDB store is created, the **encrypt** parameter in **options** specifies whether to encrypt the RDB store. The value **true** means to encrypt the RDB store, and the value **false** (default) means the opposite.
For details about the APIs, see [RDB Store](../reference/apis/js-apis-data-relationalStore.md).
```js
import relationalStore from '@ohos.data.relationalStore';
let store;
let context = getContext(this);
const STORE_CONFIG = {
name: 'RdbTest.db',
securityLevel: relationalStore.SecurityLevel.S1,
encrypt: true
};
relationalStore.getRdbStore(context, STORE_CONFIG, (err, rdbStore) => {
store = rdbStore;
if (err) {
console.error(`Failed to get RdbStore. Code:${err.code},message:${err.message}`);
return;
}
console.info(`Succeeded in getting RdbStore.`);
})
```
# Data Management Overview
## Function
Data management provides data storage, management, and synchronization capabilities. For example, you can store the Contacts application data in database for secure management and shared access, and synchronize the contacts information with a smart watch.
- Data storage: provides data persistence capabilities, which can be classified into user preferences, key-value (KV) stores, and relational database (RDB) stores by data characteristics.
- Data management: provides efficient data management capabilities, including permission management, data backup and restoration, and dataShare framework.
- Data synchronization: provides data synchronization across devices. For example, distributed data objects support sharing of memory objects across devices, and distributed databases support database access across devices.
The database stores created by an application are saved to the application sandbox. When the application is uninstalled, the database stores are automatically deleted.
## Working Principles
The data management module includes user preferences (**Preferences**), KV data management (**KV-Store**), RDB data management (**RelationalStore**), distributed data object (**DataObject**), and cross-application data management (**DataShare**). The interface layer provides standard JavaScript APIs for application development. The Frameworks&System service layer implements storage and synchronization of component data, and provides dependencies for SQLite and other subsystems.
**Figure 1** Data management architecture
![dataManagement](figures/dataManagement.jpg)
- **Preferences**: implements persistence of lightweight configuration data and supports subscription of data change notifications. Preferences are used to store application configuration information and user preference settings and do not support distributed synchronization.
- **KV-Store**: implements read, write, encryption, and manual backup of data in KV stores and notification subscription. When an application needs to use the distributed capabilities of KV stores, KV-Store sends a synchronization request to DatamgrService to implement data synchronization across devices.
- **RelationalStore**: implements addition, deletion, modification, query, encryption, manually backup of data in RDB stores, and notification subscription. When an application needs to use the distributed capabilities of an RDB store, RelationalStore sends a synchronization request to DatamgrService to implement data synchronization across devices.
- **DataObject**: independently provides distributed capabilities for the data of object structs. For the object data that is still required after the restart of an application (either the cross-device application or local device application), the **DatamgrService** implements temporary storage of the object data.
- **DataShare**: provides the data provider-consumer mode to implement addition, deletion, modification, and query of cross-application data on a device, and notification subscription. **DataShare** is not bound to any database and can interact with RDB and KV stores. You can also encapsulate your own databases for C/C++ applications.<br> In addition to the provider-consumer mode, **DataShare** provides silent access, which allows direct access to the provider's data via the DatamgrService proxy instead of starting the provider. Currently, only the RDB stores support silent access.
- **DatamgrService**: implements synchronization and cross-application sharing for other components, including cross-device synchronization of **RelationalStore** and **KV-Store**, silent access to provider data of **DataShare**, and temporary storage of **DataObject** synchronization object data.
# Persisting KV Store Data
## When to Use
The key-value (KV) database stores data in the form of KV pairs. You can use KV stores to store data organized in a simple model, for example, product names and prices or employee IDs and daily attendance. The simple data structure allows higher compatibility with different database versions and device types.
## Constraints
- For each record in a device KV store, the key cannot exceed 896 bytes and the value cannot exceed 4 MB.
- For each record in a single KV store, the key cannot exceed 1 KB and the value cannot exceed 4 MB.
- A maximum of 16 distributed KV stores can be opened simultaneously for an application.
- Blocking operations, for example, modifying UI components, are not allowed in the KV store event callbacks.
## Available APIs
The following table lists the APIs used for KV data persistence. Most of the APIs are executed asynchronously, using a callback or promise to return the result. The following table uses the callback-based APIs as an example. For more information about the APIs, see [Distributed KV Store](../reference/apis/js-apis-distributedKVStore.md).
| API| Description|
| -------- | -------- |
| createKVManager(config: KVManagerConfig): KVManager | Creates a **KvManager** instance to manage database objects.|
| getKVStore&lt;T&gt;(storeId: string, options: Options, callback: AsyncCallback&lt;T&gt;): void | Creates and obtains a KV store of the specified type.|
| put(key: string, value: Uint8Array\|string\|number\|boolean, callback: AsyncCallback&lt;void&gt;): void | Adds a KV pair of the specified type to this KV store.|
| get(key: string, callback: AsyncCallback&lt;Uint8Array\|string\|boolean\|number&gt;): void | Obtains the value of the specified key.|
| delete(key: string, callback: AsyncCallback&lt;void&gt;): void | Deletes a KV pair based on the specified key.|
## How to Develop
1. Create a **KvManager** instance to manage database objects. Example:
Stage model:
```js
// Import the module.
import distributedKVStore from '@ohos.data.distributedKVStore';
// Stage model
import UIAbility from '@ohos.app.ability.UIAbility';
let kvManager;
export default class EntryAbility extends UIAbility {
onCreate() {
let context = this.context;
const kvManagerConfig = {
context: context,
bundleName: 'com.example.datamanagertest'
};
try {
// Create a KVManager instance.
kvManager = distributedKVStore.createKVManager(kvManagerConfig);
console.info('Succeeded in creating KVManager.');
// Create and obtain the database.
} catch (e) {
console.error(`Failed to create KVManager. Code:${e.code},message:${e.message}`);
}
}
}
```
FA model:
```js
// Import the module.
import distributedKVStore from '@ohos.data.distributedKVStore';
// FA model
import featureAbility from '@ohos.ability.featureAbility';
let kvManager;
let context = featureAbility.getContext(); // Obtain the context.
const kvManagerConfig = {
context: context,
bundleName: 'com.example.datamanagertest'
};
try {
kvManager = distributedKVStore.createKVManager(kvManagerConfig);
console.info('Succeeded in creating KVManager.');
// Create and obtain the database.
} catch (e) {
console.error(`Failed to create KVManager. Code:${e.code},message:${e.message}`);
}
```
2. Create and obtain a KV store. Example:
```js
try {
const options = {
createIfMissing: true, // Whether to create a KV store when the database file does not exist. By default, a KV store is created.
createIfMissing: true, // Whether to encrypt database files. By default, database files are not encrypted.
backup: false, // Whether to back up database files. By default, the database files are backed up.
autoSync: true, // Whether to automatically synchronize database files. The value **true** means to automatically synchronize database files; the value **false** (default) means the opposite.
kvStoreType: distributedKVStore.KVStoreType.SINGLE_VERSION, // Type of the KV store to create. By default, a device KV store is created.
securityLevel: distributedKVStore.SecurityLevel.S2 // Security level of the KV store.
};
// storeId uniquely identifies a KV store.
kvManager.getKVStore('storeId', options, (err, kvStore) => {
if (err) {
console.error(`Failed to get KVStore. Code:${err.code},message:${err.message}`);
return;
}
console.info('Succeeded in getting KVStore.');
// Perform related data operations.
});
} catch (e) {
console.error(`An unexpected error occurred. Code:${e.code},message:${e.message}`);
}
```
3. Use **put()** to add data to the KV store. Example:
```js
const KEY_TEST_STRING_ELEMENT = 'key_test_string';
const VALUE_TEST_STRING_ELEMENT = 'value_test_string';
try {
kvStore.put(KEY_TEST_STRING_ELEMENT, VALUE_TEST_STRING_ELEMENT, (err) => {
if (err !== undefined) {
console.error(`Failed to put data. Code:${err.code},message:${err.message}`);
return;
}
console.info('Succeeded in putting data.');
});
} catch (e) {
console.error(`An unexpected error occurred. Code:${e.code},message:${e.message}`);
}
```
> **NOTE**
>
> The **put()** method adds a KV pair if the specified key does not exists and changes the value if the the specified key already exists.
4. Use **get()** to obtain the value of a key. Example:
```js
const KEY_TEST_STRING_ELEMENT = 'key_test_string';
const VALUE_TEST_STRING_ELEMENT = 'value_test_string';
try {
kvStore.put(KEY_TEST_STRING_ELEMENT, VALUE_TEST_STRING_ELEMENT, (err) => {
if (err !== undefined) {
console.error(`Failed to put data. Code:${err.code},message:${err.message}`);
return;
}
console.info('Succeeded in putting data.');
kvStore.get(KEY_TEST_STRING_ELEMENT, (err, data) => {
if (err !== undefined) {
console.error(`Failed to get data. Code:${err.code},message:${err.message}`);
return;
}
console.info(`Succeeded in getting data. data:${data}`);
});
});
} catch (e) {
console.error(`Failed to get data. Code:${e.code},message:${e.message}`);
}
```
5. Use **delete()** to delete the data of the specified key. Example:
```js
const KEY_TEST_STRING_ELEMENT = 'key_test_string';
const VALUE_TEST_STRING_ELEMENT = 'value_test_string';
try {
kvStore.put(KEY_TEST_STRING_ELEMENT, VALUE_TEST_STRING_ELEMENT, (err) => {
if (err !== undefined) {
console.error(`Failed to put data. Code:${err.code},message:${err.message}`);
return;
}
console.info('Succeeded in putting data.');
kvStore.delete(KEY_TEST_STRING_ELEMENT, (err) => {
if (err !== undefined) {
console.error(`Failed to delete data. Code:${err.code},message:${err.message}`);
return;
}
console.info('Succeeded in deleting data.');
});
});
} catch (e) {
console.error(`An unexpected error occurred. Code:${e.code},message:${e.message}`);
}
```
# Persisting Preferences Data
## When to Use
The **Preferences** module provides APIs for processing data in the form of key-value (KV) pairs, and supports persistence of the KV pairs when required, as well as modification and query of the data. You can use **Preferences** when you want a unique storage for global data. **Preferences** caches data in the memory, which allows fast access when the data is required. **Preferences** is recommended for storing small amount of data, such as personalized settings (font size and whether to enable the night mode) of applications.
## Working Principles
User applications call **Preference** through the JS 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.
**Figure 1** Preferences working mechanism
![preferences](figures/preferences.jpg)
## Constraints
- The key in a KV pair must be a string and cannot be empty or exceed 80 bytes.
- If the value is of the string type, it can be empty or a string not longer than 8192 bytes.
- The memory usage increases with the amount of **Preferences** data. The maximum number of data records recommended is 10,000. Otherwise, high memory overheads will be caused.
## Available APIs
The following table lists the APIs used for preferences data persistence. Most of the APIs are executed asynchronously, using a callback or promise to return the result. The following table uses the callback-based APIs as an example. For more information about the APIs, see [User Preferences](../reference/apis/js-apis-data-preferences.md).
| API| Description|
| -------- | -------- |
| getPreferences(context: Context, name: string, callback: AsyncCallback&lt;Preferences&gt;): void | Obtain a **Preferences** instance.|
| put(key: string, value: ValueType, callback: AsyncCallback&lt;void&gt;): void | Writes data to the Preferences instance. You can use **flush()** to persist the **Preferences** instance data.|
| has(key: string, callback: AsyncCallback&lt;boolean&gt;): void | Checks whether the **Preferences** instance contains a KV pair with the given key. The key cannot be empty.|
| get(key: string, defValue: ValueType, callback: AsyncCallback&lt;ValueType&gt;): void | Obtains the value of the specified key. If the value is null or not of the default value type, **defValue** is returned.|
| delete(key: string, callback: AsyncCallback&lt;void&gt;): void | Deletes the KV pair with the given key from the **Preferences** instance.|
| flush(callback: AsyncCallback&lt;void&gt;): void | Flushes the data of this **Preferences** instance to a file for data persistence.|
| on(type: 'change', callback: Callback&lt;{ key : string }&gt;): void | Subscribes to data changes of the specified key. When the value of the specified key is changed and saved by **flush()**, a callback will be invoked to return the new data.|
| off(type: 'change', callback?: Callback&lt;{ key : string }&gt;): void | Unsubscribes from data changes.|
| deletePreferences(context: Context, name: string, 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.|
## How to Develop
1. Import the **@ohos.data.preferences** module.
```js
import dataPreferences from '@ohos.data.preferences';
```
2. Obtain a **Preferences** instance. Read data from a file and load the data to a **Preferences** instance for data operations.
Stage model:
```js
import UIAbility from '@ohos.app.ability.UIAbility';
class EntryAbility extends UIAbility {
onWindowStageCreate(windowStage) {
try {
dataPreferences.getPreferences(this.context, 'mystore', (err, preferences) => {
if (err) {
console.error(`Failed to get preferences. Code:${err.code},message:${err.message}`);
return;
}
console.info('Succeeded in getting preferences.');
// Perform related data operations.
})
} catch (err) {
console.error(`Failed to get preferences. Code:${err.code},message:${err.message}`);
}
}
}
```
FA model:
```js
import featureAbility from '@ohos.ability.featureAbility';
// Obtain the context.
let context = featureAbility.getContext();
try {
dataPreferences.getPreferences(context, 'mystore', (err, preferences) => {
if (err) {
console.error(`Failed to get preferences. Code:${err.code},message:${err.message}`);
return;
}
console.info('Succeeded in getting preferences.');
// Perform related data operations.
})
} catch (err) {
console.error(`Failed to get preferences. Code is ${err.code},message:${err.message}`);
}
```
3. Write data.
Use **put()** to write data to the **Preferences** instance. After data is written, you can use **flush()** to persist the **Preferences** instance data to a file if necessary.
> **NOTE**
>
> If the specified key already exists, the **put()** method changes the value. To prevent a value from being changed by mistake, you can use **has()** to check whether the KV pair exists.
Example:
```js
try {
preferences.has('startup', function (err, val) {
if (err) {
console.error(`Failed to check the key 'startup'. Code:${err.code}, message:${err.message}`);
return;
}
if (val) {
console.info("The key 'startup' is contained.");
} else {
console.info("The key 'startup' does not contain.");
// Add a KV pair.
try {
preferences.put('startup', 'auto', (err) => {
if (err) {
console.error(`Failed to put data. Code:${err.code}, message:${err.message}`);
return;
}
console.info('Succeeded in putting data.');
})
} catch (err) {
console.error(`Failed to put data. Code: ${err.code},message:${err.message}`);
}
}
})
} catch (err) {
console.error(`Failed to check the key 'startup'. Code:${err.code}, message:${err.message}`);
}
```
4. Read data.
Use **get()** 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. Example:
```js
try {
preferences.get('startup', 'default', (err, val) => {
if (err) {
console.error(`Failed to get value of 'startup'. Code:${err.code}, message:${err.message}`);
return;
}
console.info(`Succeeded in getting value of 'startup'. val: ${val}.`);
})
} catch (err) {
console.error(`Failed to get value of 'startup'. Code:${err.code}, message:${err.message}`);
}
```
5. Deletes data.
Use delete() to delete a KV pair.<br>Example:
```js
try {
preferences.delete('startup', (err) => {
if (err) {
console.error(`Failed to delete the key 'startup'. Code:${err.code}, message:${err.message}`);
return;
}
console.info("Succeeded in deleting the key 'startup'.");
})
} catch (err) {
console.error(`Failed to delete the key 'startup'. Code:${err.code}, message:${err.message}`);
}
```
6. Persist data.
You can use **flush()** to persist the data held in a **Preferences** instance to a file. Example:
```js
try {
preferences.flush((err) => {
if (err) {
console.error(`Failed to flush. Code:${err.code}, message:${err.message}`);
return;
}
console.info('Succeeded in flushing.');
})
} catch (err) {
console.error(`Failed to flush. Code:${err.code}, message:${err.message}`);
}
```
7. Subscribe to data changes.
Specify an observer as the callback to return the data changes for an application. When the value of the subscribed key is changed and saved by **flush()**, the observer callback will be invoked to return the new data. Example:
```js
let observer = function (key) {
console.info('The key' + key + 'changed.');
}
preferences.on('change', observer);
// The data is changed from 'auto' to 'manual'.
preferences.put('startup', 'manual', (err) => {
if (err) {
console.error(`Failed to put the value of 'startup'. Code:${err.code},message:${err.message}`);
return;
}
console.info("Succeeded in putting the value of 'startup'.");
preferences.flush((err) => {
if (err) {
console.error(`Failed to flush. Code:${err.code}, message:${err.message}`);
return;
}
console.info('Succeeded in flushing.');
})
})
```
8. Delete a **Preferences** instance from the memory.
Use **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.
> **NOTE**
>
> - The deleted **Preferences** instance cannot be used for data operations. Otherwise, data inconsistency will be caused.
>
> - The deleted data and files cannot be restored.
Example:
```js
try {
dataPreferences.deletePreferences(this.context, 'mystore', (err, val) => {
if (err) {
console.error(`Failed to delete preferences. Code:${err.code}, message:${err.message}`);
return;
}
console.info('Succeeded in deleting preferences.');
})
} catch (err) {
console.error(`Failed to delete preferences. Code:${err.code}, message:${err.message}`);
}
```
# Persisting RDB Store Data
## When to Use
A relational database (RDB) store is used to store data in complex relational models, such as the student information including names, student IDs, and scores of each subject, or employee information including names, employee IDs, and positions, based on SQLite. The data is more complex than key-value (KV) pairs due to strict mappings. You can use **RelationalStore** to implement persistence of this type of data.
## Basic Concepts
- **Predicates**: A representation of the property or feature of a data entity, or the relationship between data entities. It is used to define operation conditions.
- **ResultSet**: a set of query results, which allows access to the required data in flexible modes.
## Working Principles
**RelationalStore** provides APIs for applications to perform data operations. With SQLite as the underlying persistent storage engine, **RelationalStore** provides SQLite database features, including transactions, indexes, views, triggers, foreign keys, parameterized queries, prepared SQL statements, and more.
**Figure 1** Working mechanism
![relationStore_local](figures/relationStore_local.jpg)
## Constraints
- The default logging mode is Write Ahead Log (WAL), and the default flushing mode is **FULL** mode.
- An RDB store can be connected to a maximum of four connection pools for user read operations.
- To ensure data accuracy, only one write operation is allowed at a time.
- Once an application is uninstalled, related database files and temporary files on the device are automatically deleted.
## Available APIs
The following table lists the APIs used for RDB data persistence. Most of the APIs are executed asynchronously, using a callback or promise to return the result. The following table uses the callback-based APIs as an example. For more information about the APIs, see [RDB Store](../reference/apis/js-apis-data-relationalStore.md).
| API| Description|
| -------- | -------- |
| getRdbStore(context: Context, config: StoreConfig, callback: AsyncCallback&lt;RdbStore&gt;): void | Obtains a **RdbStore** instance to implement RDB store operations. You can set **RdbStore** parameters based on actual requirements and use **RdbStore** APIs to perform data operations.|
| executeSql(sql: string, bindArgs: Array&lt;ValueType&gt;, callback: AsyncCallback&lt;void&gt;):void | Executes an SQL statement that contains specified arguments but returns no value.|
| insert(table: string, values: ValuesBucket, callback: AsyncCallback&lt;number&gt;):void | Inserts a row of data into a table.|
| update(values: ValuesBucket, predicates: RdbPredicates, callback: AsyncCallback&lt;number&gt;):void | Updates data in the RDB store based on the specified **RdbPredicates** instance.|
| delete(predicates: RdbPredicates, callback: AsyncCallback&lt;number&gt;):void | Deletes data from the RDB store based on the specified **RdbPredicates** instance.|
| query(predicates: RdbPredicates, columns: Array&lt;string&gt;, callback: AsyncCallback&lt;ResultSet&gt;):void | Queries data in the RDB store based on specified conditions.|
| deleteRdbStore(context: Context, name: string, callback: AsyncCallback&lt;void&gt;): void | Deletes an RDB store.|
## How to Develop
1. Obtain an **RdbStore** instance.<br> Example:
Stage model:
```js
import relationalStore from '@ohos.data.relationalStore'; // Import the module.
import UIAbility from '@ohos.app.ability.UIAbility';
class EntryAbility extends UIAbility {
onWindowStageCreate(windowStage) {
const STORE_CONFIG = {
name: 'RdbTest.db', // Database file name.
securityLevel: relationalStore.SecurityLevel.S1 // Database security level.
};
const SQL_CREATE_TABLE ='CREATE TABLE IF NOT EXISTS EMPLOYEE (ID INTEGER PRIMARY KEY AUTOINCREMENT, NAME TEXT NOT NULL, AGE INTEGER, SALARY REAL, CODES BLOB)'; // SQL statement for creating a data table.
relationalStore.getRdbStore(this.context, STORE_CONFIG, (err, store) => {
if (err) {
console.error(`Failed to get RdbStore. Code:${err.code}, message:${err.message}`);
return;
}
console.info(`Succeeded in getting RdbStore.`);
store.executeSql(SQL_CREATE_TABLE); // Create a data table.
// Perform operations such as adding, deleting, modifying, and querying data in the RDB store.
});
}
}
```
FA model:
```js
import relationalStore from '@ohos.data.relationalStore'; // Import the module.
import featureAbility from '@ohos.ability.featureAbility';
// Obtain the context.
let context = featureAbility.getContext();
const STORE_CONFIG = {
name: 'RdbTest.db', // Database file name.
securityLevel: relationalStore.SecurityLevel.S1 // Database security level.
};
const SQL_CREATE_TABLE ='CREATE TABLE IF NOT EXISTS EMPLOYEE (ID INTEGER PRIMARY KEY AUTOINCREMENT, NAME TEXT NOT NULL, AGE INTEGER, SALARY REAL, CODES BLOB)'; // SQL statement for creating a data table.
relationalStore.getRdbStore(context, STORE_CONFIG, (err, store) => {
if (err) {
console.error(`Failed to get RdbStore. Code:${err.code}, message:${err.message}`);
return;
}
console.info(`Succeeded in getting RdbStore.`);
store.executeSql(SQL_CREATE_TABLE); // Create a data table.
// Perform operations such as adding, deleting, modifying, and querying data in the RDB store.
});
```
> **NOTE**
>
> - The RDB store created by an application varies with the context. Multiple RDB stores are created for the same database name with different application contexts. For example, each UIAbility has its own context.
>
> - When an application calls **getRdbStore()** to obtain an RDB store instance for the first time, the corresponding database file is generated in the application sandbox. If you want to move the files of an RDB store to another place for view, you must also move the temporary files with finename extensions **-wal** or **-shm** in the same directory. Once an application is uninstalled, the database files and temporary files generated by the application on the device are also removed.
2. Use **insert()** to insert data to the RDB store. Example:
```js
const valueBucket = {
'NAME': 'Lisa',
'AGE': 18,
'SALARY': 100.5,
'CODES': new Uint8Array([1, 2, 3, 4, 5])
};
store.insert('EMPLOYEE', valueBucket, (err, rowId) => {
if (err) {
console.error(`Failed to insert data. Code:${err.code}, message:${err.message}`);
return;
}
console.info(`Succeeded in inserting data. rowId:${rowId}`);
})
```
> **NOTE**
>
> **RelationalStore** does not provide explicit flush operations for data persistence. Data inserted by **insert()** is stored in files persistently.
3. Modify or delete data based on the specified **Predicates** instance.
Use **update()** to modify data and **delete()** to delete data. Example:
```js
// Modify data.
const valueBucket = {
'NAME': 'Rose',
'AGE': 22,
'SALARY': 200.5,
'CODES': new Uint8Array([1, 2, 3, 4, 5])
};
let predicates = new relationalStore.RdbPredicates('EMPLOYEE'); // Create predicates for the table named EMPLOYEE.
predicates.equalTo('NAME', 'Lisa'); // Modify the data of Lisa in the EMPLOYEE table to the specified data.
store.update(valueBucket, predicates, (err, rows) => {
if (err) {
console.error(`Failed to update data. Code:${err.code}, message:${err.message}`);
return;
}
console.info(`Succeeded in updating data. row count: ${rows}`);
})
// Delete data.
let predicates = new relationalStore.RdbPredicates('EMPLOYEE');
predicates.equalTo('NAME', 'Lisa');
store.delete(predicates, (err, rows) => {
if (err) {
console.error(`Failed to delete data. Code:${err.code}, message:${err.message}`);
return;
}
console.info(`Delete rows: ${rows}`);
})
```
4. Query data based on the conditions specified by **Predicates**.
Use **query()** to query data. The data obtained is returned in a **ResultSet** object. Example:
```js
let predicates = new relationalStore.RdbPredicates('EMPLOYEE');
predicates.equalTo('NAME', 'Rose');
store.query(predicates, ['ID', 'NAME', 'AGE', 'SALARY', 'CODES'], (err, resultSet) => {
if (err) {
console.error(`Failed to query data. Code:${err.code}, message:${err.message}`);
return;
}
console.info(`ResultSet column names: ${resultSet.columnNames}`);
console.info(`ResultSet column count: ${resultSet.columnCount}`);
})
```
> **NOTE**
>
> Use **close()** to close the **ResultSet** that is no longer used in a timely manner so that the memory allocated can be released.
5. Delete the RDB store.
Use **deleteRdbStore()** to delete the RDB store and related database files. Example:
Stage model:
```js
import UIAbility from '@ohos.app.ability.UIAbility';
class EntryAbility extends UIAbility {
onWindowStageCreate(windowStage) {
relationalStore.deleteRdbStore(this.context, 'RdbTest.db', (err) => {
if (err) {
console.error(`Failed to delete RdbStore. Code:${err.code}, message:${err.message}`);
return;
}
console.info('Succeeded in deleting RdbStore.');
});
}
}
```
FA model:
```js
import featureAbility from '@ohos.ability.featureAbility';
// Obtain the context.
let context = featureAbility.getContext();
relationalStore.deleteRdbStore(context, 'RdbTest.db', (err) => {
if (err) {
console.error(`Failed to delete RdbStore. Code:${err.code}, message:${err.message}`);
return;
}
console.info('Succeeded in deleting RdbStore.');
});
```
# Data Reliability and Security Overview
## Introduction
During system running, a database fault may occur due to storage damage, insufficient storage space, file system permission, or system power-off. The database fault may cause data loss. For example, the database corruption of Contacts causes the loss of Contacts data. The data management subsystem provides the following solutions and capabilities to ensure data reliability and security:
- Data backup and restoration: Critical data (such as the bank information) can be backed up and restored from the backup to prevent data loss.
- Database encryption: The database that stores sensitive information, such as authentication credentials and financial data, can be encrypted to improve data security.
- Access control by device and data level: The access to data across devices is controlled based on the device security level and data security labels.
In addition, the backup database is stored in the application sandbox. When the storage space is insufficient, you can delete the local database backup to release space.
## Basic Concepts
Before developing functions related to data reliability and security, understand the following concepts.
### Database Backup and Restoration
- Database backup: OpenHarmony provides full backup of database files.
When backing up a database, you only need to invoke the backup API of the database, without closing the database.
- Database restoration: You can restore a database from a database backup file.
### Database Encryption
The entire database file can be encrypted to enhance the database security.
### Data Rating
In distributed scenarios, the access to data is controlled based on the device security level and data security labels.
A higher data security label and device security level indicate stricter encryption and access control measures and higher data security.
## Working Principles
### Database Backup and Restoration Mechanism
The data of a database is backed up to the specified file. Subsequent operations on the database do not affect the backup file. The database is overwritten by the specified backup file only when a restoration is performed.
- KV store backup directory: **/data/service/el1(el2)/public/database/...{appId}/kvdb/backup/...{storeId}**
- RDB store backup directory: **/data/app/el1(el2)/100/database/...{bundlename}/rdb**
### Database Encryption Mechanism
When encrypting a database, you do not need to pass in the key for encryption. The only thing you need to do is set the database encryption status. The system automatically calls the [HUKS APIs](../reference/apis/js-apis-huks.md) to generate a key and encrypt the database.
## Constraints
- The database encryption key is automatically changed once a year.
- A maximum of five backup files can be retained for a KV store.
- Automatic backup of a KV store must be performed when the device is charging and the screen is off.
# Cross-Device Synchronization of Distributed Data Objects
## When to Use
To implement traditional data synchronization between devices, you need to design the message processing logic, including setting up a communication link, sending, receiving, and processing messages, retry mechanism upon errors, and resolving data conflicts. The workload is heavy. In addition, the debugging complexity increases with the number of devices.
The device status, message sending progress, and data transmitted are variables. If these variables support global access, they can be accessed as local variables on difference devices. This simplifies data synchronization between multiple devices.
The distributed data object (**DataObject**) implements global access to variables. **DataObject** provides basic data object management capabilities and distributed capabilities. You can use the APIs to create, query, delete, and modify in-memory objects and subscribe to event notifications. OpenHarmony also provides easy-to-use JS APIs for distributed application scenarios to easily implement cross-device data collaboration for the same application. In addition, object status and data changes on different devices can be observed. This feature implements data object collaboration for the same application between multiple devices that form a Super Device. **DataObject** greatly reduces the development workloads compared with the traditional mode.
## Basic Concepts
- Distributed in-memory database<br>
The distributed in-memory database caches data in the memory so that applications can quickly access data without persisting data. If the database is closed, the data is not retained.
- Distributed data object
A distributed data object is an encapsulation of the JS object type. Each distributed data object instance creates a data table in the in-memory database. The in-memory databases created for different applications are isolated from each other. Reading data from and writing data to a distributed data object are mapped to the **get()** and **put()** operations in the corresponding database, respectively.
The distributed data object can be in the following states in its lifecycle:
- **Uninitialized**: The distributed data object is not instantiated or has been destroyed.
- **Local**: The data table is created, but the data cannot be synchronized.
- **Distributed**: The data table is created, and there are at least two online devices with the same session ID. In this case, data can be synchronized across devices. If a device is offline or the session ID is empty, the distributed data object changes to the local state.
## Working Principles
**Figure 1** Working mechanism
![distributedObject](figures/distributedObject.jpg)
The distributed data objects are encapsulated into JS objects in distributed in-memory databases. This allows the distributed data objects to be operated in the same way as local variables. The system automatically implements cross-device data synchronization.
### JS Object Storage and Encapsulation Mechanism
- An in-memory database is created for each distributed data object instance and identified by a session ID (**SessionId**). The in-memory databases created for different applications are isolated from each other.
- When a distributed data object is instantiated, all properties of the object are traversed recursively. **Object.defineProperty** is used to define the **set()** and **get()** methods of all properties. The **set()** and **get()** methods correspond to the **put** and **get** operations of a record in the database, respectively. **Key** specifies the property name, and **Value** specifies the property value.
- When a distributed data object is read or written, the **set()** and **get()** methods are automatically called to perform the related operations to the database.
**Table 1** Correspondence between a distributed data object and a distributed database
| Distributed Data Object Instance| Object Instance| Property Name| Property Value|
| -------- | -------- | -------- | -------- |
| Distributed in-memory database| Database identified by **sessionID**| Key of a record in the database| Value of a record in the database|
### Cross-Device Synchronization and Data Change Notification Mechanism
The distributed data object is used to implement data synchronization between objects. You can create a distributed data object and set **sessionID** for the devices on a trusted network. The distributed data objects with the same **sessionID** on different devices can synchronize data with each other.
As shown in the following figure, distributed data object 1 on device A and device B have the same session ID **session1**. The synchronization relationship of session1 is established between the two objects.
**Figure 2** Object synchronization relationship
![distributedObject_sync](figures/distributedObject_sync.jpg)
For each device, only one object can be added to a synchronization relationship. As shown in the preceding figure, distributed data object 2 of device A cannot be added to session 1 because distributed data object 1 of device A has been added to session 1.
After the synchronization relationship is established, each session has a copy of shared object data. The distributed data objects added to the same session support the following operations:
(1) Reading or modifying the data in the session.
(2) Listening for data changes made by other devices.
(3) Listening for status changes, such as the addition and removal of other devices.
### Minimum Unit to Synchronize
Attribute is the minimum unit to synchronize in distributed data objects. For example, object 1 in the following figure has three attributes: name, age, and parents. If one of the attributes is changed, only the changed attribute needs to be synchronized.
**Figure 3** Synchronization of distributed data objects
![distributedObject_syncView](figures/distributedObject_syncView.jpg)
### Object Persistence Mechanism
Distributed data objects run in the process space of applications. When the data of a distributed data object is persisted in the distributed database, the data will not be lost after the application exits.
You need to persist distributed data objects in the following scenarios:
- Enable an application to retrieve the exact same data after it is opened again. In this case, you need to persist the distributed data object (for example, object 1). After the application is opened again, create a distributed data object (for example, object 2) and set the session ID of object 1 for object 2. Then, the application can retrieve the data of object 1.
- Enable an application opened on another device to retrieve the exact same data. In this case, you need to persist the distributed data object (for example, object 1) on device A and synchronize the data to device B. Then, create a distributed data object (for example, object 2) and set the session ID of object 1 for object 2. When the application is opened on device B, it can retrieve the same application data used on device A before the application is closed.
## Constraints
- Data synchronization can be implemented across devices only for the applications with the same **bundleName**.
- Data can be synchronized only for the distributed data objects with the same **sessionID** of the same application.
- Each distributed data object occupies 100 KB to 150 KB of memory. Therefore, you are advised not to create too many distributed data objects.
- The maximum size of a distributed data object is 500 KB.
- It takes about 50 ms from the time when 1 KB of data starts to be modified on a device to the time when another device receives a data change notification.
- A maximum of 16 distributed data object instances can be created for an application.
- For optimal performance and user experience, the maximum number of devices for data collaboration is 3.
- For the distributed data object of the complex type, only the root attribute can be modified. The subordinate attributes cannot be modified.
- Only JS APIs are supported.
## Available APIs
The following table lists the APIs for cross-device synchronization of distributed data objects. Most of the interfaces are executed asynchronously, using a callback or promise to return the result. The following table uses the callback-based APIs as an example. For more information about the APIs, see [Distributed Data Object](../reference/apis/js-apis-data-distributedobject.md).
| API| Description|
| -------- | -------- |
| create(context: Context, source: object): DataObject | Creates a distributed data object instance.|
| genSessionId(): string | Generates a session ID for distributed data objects.|
| setSessionId(sessionId: string, callback: AsyncCallback&lt;void&gt;): void | Sets a session ID for data synchronization. Automatic synchronization is performed for devices with the same session ID on a trusted network.|
| setSessionId(callback: AsyncCallback&lt;void&gt;): void | Exits all sessions.|
| on(type: 'change', callback: Callback&lt;{ sessionId: string, fields: Array&lt;string&gt; }&gt;): void | Subscribes to data changes of this distributed data object.|
| on(type: 'status', callback: Callback&lt;{ sessionId: string, networkId: string, status: 'online' \| 'offline' }&gt;): void | Subscribes to status changes of this distributed data object.|
| save(deviceId: string, callback: AsyncCallback&lt;SaveSuccessResponse&gt;): void | Saves a distributed data object.|
| revokeSave(callback: AsyncCallback&lt;RevokeSaveSuccessResponse&gt;): void | Revokes the save operation of the distributed data object.|
## How to Develop
The following example demonstrates how to implement a distributed data object synchronization.
1. Import the **@ohos.data.distributedDataObject** module.
```js
import distributedDataObject from '@ohos.data.distributedDataObject';
```
2. Request permissions.
1. Request the **ohos.permission.DISTRIBUTED_DATASYNC** permission. For details, see [Declaring Permissions in the Configuration File](../security/accesstoken-guidelines.md#declaring-permissions-in-the-configuration-file).
2. Display a dialog box to ask authorization from the user when the application is started for the first time. For details, see [Requesting User Authorization](../security/accesstoken-guidelines.md#requesting-user-authorization).
3. Creates a distributed data object instance.
Stage model:
```js
// Import the module.
import distributedDataObject from '@ohos.data.distributedDataObject';
import UIAbility from '@ohos.app.ability.UIAbility';
class EntryAbility extends UIAbility {
onWindowStageCreate(windowStage) {
// Create a distributed data object, which contains attributes of the string, number, boolean, and object types.
let localObject = distributedDataObject.create(this.context, {
name: 'jack',
age: 18,
isVis: false,
parent: { mother: 'jack mom', father: 'jack Dad' },
list: [{ mother: 'jack mom' }, { father: 'jack Dad' }]
});
}
}
```
FA model:
```js
// Import the module.
import distributedDataObject from '@ohos.data.distributedDataObject';
import featureAbility from '@ohos.ability.featureAbility';
// Obtain the context.
let context = featureAbility.getContext();
// Create a distributed data object, which contains attributes of the string, number, boolean, and object types.
let localObject = distributedDataObject.create(context, {
name: 'jack',
age: 18,
isVis: false,
parent: { mother: 'jack mom', father: 'jack Dad' },
list: [{ mother: 'jack mom' }, { father: 'jack Dad' }]
});
```
4. Set the same session ID for the distributed data objects for data synchronization. The data objects in the synchronization network include the local and remote objects.
```js
// Set a session ID, for example, 123456, for device 1.
let sessionId = '123456';
localObject.setSessionId(sessionId);
// Set the same session ID for device 2.
// Create a distributed data object, which contains attributes of the string, number, boolean, and object types.
let remoteObject = distributedDataObject.create(this.context, {
name: undefined,
age: undefined, // undefined indicates that the data comes from the peer end.
isVis: true,
parent: undefined,
list: undefined
});
// After learning that the device goes online, the remote object synchronizes data. That is, name changes to jack and age to 18.
remoteObject.setSessionId(sessionId);
```
5. Observe data changes of a distributed data object. You can subscribe to data changes of the remote object. When the data in the remote object changes, a callback will be invoked to return a data change event.
```js
function changeCallback(sessionId, changeData) {
console.info(`change: ${sessionId}`);
if (changeData !== null && changeData !== undefined) {
changeData.forEach(element => {
console.info(`The element ${localObject[element]} changed.`);
});
}
}
// To refresh the page in changeCallback, correctly bind (this) to the changeCallback.
localObject.on("change", this.changeCallback.bind(this));
```
6. Modify attributes of the distributed data object. The object attributes support basic data types (number, Boolean, and string) and complex data types (array and nested basic types).
```js
localObject.name = 'jack1';
localObject.age = 19;
localObject.isVis = false;
localObject.parent = { mother: 'jack1 mom', father: 'jack1 Dad' };
localObject.list = [{ mother: 'jack1 mom' }, { father: 'jack1 Dad' }];
```
> **NOTE**
>
> For the distributed data object of the complex type, only the root attribute can be modified. The subordinate attributes cannot be modified.
```js
// Supported modification.
localObject.parent = { mother: 'mom', father: 'dad' };
// Modification not supported.
localObject.parent.mother = 'mom';
```
7. Access a distributed data object. Obtain the distributed data object attributes, which are the latest data on the network.
```js
console.info(`name:${localObject['name']}`);
```
8. Unsubscribe from data changes. You can specify the callback to unregister. If you do not specify the callback, all data change callbacks of the distributed data object will be unregistered.
```js
// Unregister this.changeCallback.
localObject.off('change', this.changeCallback);
// Unregister all data change callbacks.
localObject.off('change');
```
9. Subscribes to status changes of a distributed data object. A callback will be invoked to report the status change when the target distributed data object goes online or offline.
```js
function statusCallback(sessionId, networkId, status) {
// Service processing.
}
localObject.on('status', this.statusCallback);
```
10. Save a distributed data object and revoke the data saving operation.
```js
// Save the data object if the device on the network needs to retrieve the object data after the application exits.
localObject.save('local').then((result) => {
console.info(`Succeeded in saving. SessionId:${result.sessionId},version:${result.version},deviceId:${result.deviceId}`);
}).catch((err) => {
console.error(`Failed to save. Code:${err.code},message:${err.message}`);
});
// Revoke the save of a distributed data object.
localObject.revokeSave().then((result) => {
console.info(`Succeeded in revokeSaving. Session:${result.sessionId}`);
}).catch((err) => {
console.error(`Failed to revokeSave. Code:${err.code},message:${err.message}`);
});
```
11. Unsubscribe from the status changes of a distributed data object. You can specify the callback to unregister. If you do not specify the callback, this API unregisters all status change callbacks of this distributed data object.
```js
// Unregister this.statusCallback.
localObject.off('status', this.statusCallback);
// Unregister all status change callbacks.
localObject.off('status');
```
12. Remove a distributed data object from the synchronization network. The data of the removed distributed data object will not be synchronized to other devices.
```js
localObject.setSessionId(() => {
console.info('leave all lession.');
});
```
# Cross-Device Synchronization of KV Stores
## When to Use
KV Stores are suitable for storing service data with simple relationships. It provides higher read and write performance than the SQL database. KV stores are widely used because the simplicity of the KV data model poses fewer database version compatibility issues in distributed scenarios and simplifies conflict handling in data synchronization.
## Basic Concepts
Before implementing cross-device synchronization of KV stores, understand the following concepts:
### Single KV Store
In a single KV store, data is saved in the unit of a single entry. When data is modified locally, the data entry is updated no matter whether it has been synchronized. Only one copy of data is retained globally for multiple devices. The data of the latest time is kept for the same entry (with the same primary code) of multiple devices. The data in single KV stores is not differentiated by device. If the data modified on multiple devices has the same key, the value will be overwritten. For the data written or modified locally, the data with the latest time is synchronized to other devices. Single KV stores are used to store information, such as the Contacts and weather application data.
![singleKVStore](figures/singleKVStore.jpg)
### Device KV Store
In a device KV store, the local device ID is added before the key of the KV pair stored by an application. In this way, the data of different devices is isolated. Data is managed by device and can be queried by device.
The underlying devices manage the data by device. The device KV stores support distributed data query by device, but do not support modification of the data synchronized from peer devices. Device KV stores are used to store the data that needs to be accessed by device, such as the Gallery thumbnails.
![deviceKVStore](figures/deviceKVStore.jpg)
## Synchronization Types
The **DatamgrService** provides the following synchronization types:
- Manual synchronization: The application calls **sync()** to trigger a synchronization. The list of devices to be synchronized and the synchronization mode must be specified. The synchronization mode can be **PULL_ONLY** (pulling remote data to the local end), **PUSH_ONLY** (pushing local data to the remote end), or **PUSH_PULL** (pushing local data to the remote end and pulling remote data to the local end). You can use the [**sync()** with the **query** parameter](../reference/apis/js-apis-distributedKVStore.md#sync-1) to synchronize the data that meets the specified conditions. The manual synchronization is available only for system applications.
- Automatic synchronization: The distributed database automatically pushes local data to the remote end and pulls remote data to the local end. An automatic synchronization is triggered when a device goes online or an application updates data.
## Working Principles
After completing device discovery and authentication, the underlying communication component notifies the application that the device goes online. The **DatamgrService** then establishes an encrypted transmission channel to synchronize data between the two devices.
### Cross-Device Data Synchronization Mechanism
![kvStore](figures/kvStore.jpg)
When **put()** or **delete()** is called successfully, an automatic synchronization is triggered. The distributed data is sent to the peer device through the communication adaptation layer for synchronization.
If **sync()** is called successfully, a manual synchronization is triggered to send distributed data to the peer device through the communication adaptation layer.
### Data Change Notification Mechanism
When data is added, deleted, or modified, a notification is sent to the subscriber. The notifications can be classified into the following types:
- Local data change notification: subscription of the application data changes on the local device. When the data in the local KV store is added, deleted, or modified in the database, a notification is received.
- Distributed data change notification: subscription of the application data changes of other devices in the network. When the data in the local KV store changes after being synchronized with data from another device in the same network, a notification is received.
## Constraints
- For each record in a device KV store, the key cannot exceed 896 bytes and the value cannot exceed 4 MB.
- For each record in a single KV store, the key cannot exceed 1 KB and the value cannot exceed 4 MB.
- The KV stores do not support custom conflict resolution policies for applications.
- A maximum of 16 distributed KV stores can be opened simultaneously for an application.
- Each KV store supports a maximum of eight callbacks for subscription of data change notifications.
- The manual synchronization is available only for system applications.
## Available APIs
The following table lists the APIs for cross-device data synchronization of the single KV store. Most of the APIs are executed asynchronously, using a callback or promise to return the result. The following table uses the callback-based APIs as an example. For more information about the APIs, see [Distributed KV Store](../reference/apis/js-apis-distributedKVStore.md).
| API| Description|
| -------- | -------- |
| createKVManager(config: KVManagerConfig): KVManager | Creates a **KvManager** instance to manage database objects.|
| getKVStore&lt;T&gt;(storeId: string, options: Options, callback: AsyncCallback&lt;T&gt;): void | Creates and obtains a KV store of the specified type.|
| put(key: string, value: Uint8Array\|string\|number\|boolean, callback: AsyncCallback&lt;void&gt;): void | Inserts and updates data.|
| on(event: 'dataChange', type: SubscribeType, listener: Callback&lt;ChangeNotification&gt;): void | Subscribes to data changes in the KV store.|
| get(key: string, callback: AsyncCallback&lt;boolean \| string \| number \| Uint8Array&gt;): void | Queries the value of the specified key.|
| sync(deviceIds: string[], mode: SyncMode, delayMs?: number): void | Triggers a manual synchronization of the KV store.|
## How to Develop
The following uses a single KV store as an example to describe how to implement cross-device data synchronization. The following describes the development process.
![kvStore_development_process](figures/kvStore_development_process.png)
> **NOTE**
>
> The data on a device can be synchronized only to the devices whose data security labels are not higher than the security level of the device. For details, see [Access Control Mechanism in Cross-Device Synchronization](sync-app-data-across-devices-overview.md#access-control-mechanism-in-cross-device-synchronization).
1. Import the module.
```js
import distributedKVStore from '@ohos.data.distributedKVStore';
```
2. Request permissions.
1. Request the **ohos.permission.DISTRIBUTED_DATASYNC** permission. For details, see [Declaring Permissions in the Configuration File](../security/accesstoken-guidelines.md#declaring-permissions-in-the-configuration-file).
2. Display a dialog box to ask authorization from the user when the application is started for the first time. For details, see [Requesting User Authorization](../security/accesstoken-guidelines.md#requesting-user-authorization).
3. Create a **KvManager** instance based on the specified **KvManagerConfig** object.
1. Create a **kvManagerConfig** object based on the application context.
2. Create a **KvManager** instance.
```js
// Obtain the context of the stage model.
import UIAbility from '@ohos.app.ability.UIAbility';
let kvManager;
let context = null;
class EntryAbility extends UIAbility {
onWindowStageCreate(windowStage) {
context = this.context;
}
}
// Obtain the context of the FA model.
import featureAbility from '@ohos.ability.featureAbility';
let context = featureAbility.getContext();
// Construct a kvManager instance.
try {
const kvManagerConfig = {
bundleName: 'com.example.datamanagertest',
context: context
}
kvManager = distributedKVStore.createKVManager(kvManagerConfig);
console.info('Succeeded in creating KVManager.');
// Create and obtain the KV store.
} catch (e) {
console.error(`Failed to create KVManager. Code:${e.code},message:${e.message}`);
}
```
4. Obtain the KV store of the specified type.
1. Declare the ID of the distributed KV store to create.
2. Disable the auto synchronization function (**autoSync:false**) to facilitate subsequent verification of the synchronization function. If synchronization is required, call the **sync()** interface.
```js
try {
const options = {
createIfMissing: true,
encrypt: false,
backup: false,
autoSync: false,
// If kvStoreType is left empty, a device KV store is created by default.
kvStoreType: distributedKVStore.KVStoreType.SINGLE_VERSION,
// Device KV store: kvStoreType: distributedKVStore.KVStoreType.DEVICE_COLLABORATION,
securityLevel: distributedKVStore.SecurityLevel.S1
};
kvManager.getKVStore('storeId', options, (err, kvStore) => {
if (err) {
console.error(`Failed to get KVStore: Code:${err.code},message:${err.message}`);
return;
}
console.info('Succeeded in getting KVStore.');
// Perform related data operations.
});
} catch (e) {
console.error(`An unexpected error occurred. Code:${e.code},message:${e.message}`);
}
```
5. Subscribe to changes of distributed data.
```js
try {
kvStore.on('dataChange', distributedKVStore.SubscribeType.SUBSCRIBE_TYPE_ALL, (data) => {
console.info(`dataChange callback call data: ${data}`);
});
} catch (e) {
console.error(`An unexpected error occurred. code:${e.code},message:${e.message}`);
}
```
6. Write data to the single KV store.
1. Construct the key and value to be written to the single KV store.
2. Write KV pairs to the single KV store.
```js
const KEY_TEST_STRING_ELEMENT = 'key_test_string';
const VALUE_TEST_STRING_ELEMENT = 'value_test_string';
try {
kvStore.put(KEY_TEST_STRING_ELEMENT, VALUE_TEST_STRING_ELEMENT, (err) => {
if (err !== undefined) {
console.error(`Failed to put data. Code:${err.code},message:${err.message}`);
return;
}
console.info('Succeeded in putting data.');
});
} catch (e) {
console.error(`An unexpected error occurred. Code:${e.code},message:${e.message}`);
}
```
7. Query data in the single KV store.
1. Construct the key to be queried from the single KV store.
2. Query data from the single KV store.
```js
const KEY_TEST_STRING_ELEMENT = 'key_test_string';
const VALUE_TEST_STRING_ELEMENT = 'value_test_string';
try {
kvStore.put(KEY_TEST_STRING_ELEMENT, VALUE_TEST_STRING_ELEMENT, (err) => {
if (err !== undefined) {
console.error(`Failed to put data. Code:${err.code},message:${err.message}`);
return;
}
console.info('Succeeded in putting data.');
kvStore.get(KEY_TEST_STRING_ELEMENT, (err, data) => {
if (err != undefined) {
console.error(`Failed to get data. Code:${err.code},message:${err.message}`);
return;
}
console.info(`Succeeded in getting data. Data:${data}`);
});
});
} catch (e) {
console.error(`Failed to get data. Code:${e.code},message:${e.message}`);
}
```
8. Synchronize data to other devices.
Select the devices to be synchronized with data and the synchronization mode. The user needs to confirm the synchronization mode when the application is started for the first time.
> **NOTE**
>
> If manual synchronization is used, **deviceIds** is obtained by using [devManager.getTrustedDeviceListSync](../reference/apis/js-apis-device-manager.md#gettrusteddevicelistsync). The APIs of the **deviceManager** module are all system interfaces and available only to system applications.
```js
import deviceManager from '@ohos.distributedHardware.deviceManager';
let devManager;
// create deviceManager
deviceManager.createDeviceManager('bundleName', (err, value) => {
if (!err) {
devManager = value;
// deviceIds is obtained by devManager.getTrustedDeviceListSync.
let deviceIds = [];
if (devManager !== null) {
// The ohos.permission.ACCESS_SERVICE_DM permission is required. This permission is available only for system applications.
let devices = devManager.getTrustedDeviceListSync();
for (let i = 0; i < devices.length; i++) {
deviceIds[i] = devices[i].deviceId;
}
}
try {
// 1000 indicates the maximum delay, in ms.
kvStore.sync(deviceIds, distributedKVStore.SyncMode.PUSH_ONLY, 1000);
} catch (e) {
console.error(`An unexpected error occurred. Code:${e.code},message:${e.message}`);
}
}
});
```
# Cross-Device Synchronization of RDB Stores
## When to Use
When creating a data table, you can set the table to support cross-device access. You can also use APIs to move the data to be accessed across devices to a distributed data.
## Basic Concepts
OpenHamony supports synchronization of the relational data of an application across multiple devices.
- Distributed table list<br>After a table is created for an application in an RDB store, you can set it as a distributed table. When querying the RDB store of a remote device, you can obtain the distributed table name of the remote device based on the local table name.
- Synchronization mode<br>Data can be synchronized between devices in either of the following ways: <br>- Pushing data from a local device to a remote device. <br>- Pulling data from a remote device to a local device.
## Working Principles
After completing device discovery and authentication, the underlying communication component notifies the application that the device goes online. The **DatamgrService** then establishes an encrypted transmission channel to synchronize data between the two devices.
### Cross-Device Data Synchronization Mechanism
![relationalStore_sync](figures/relationalStore_sync.jpg)
After writing data to an RDB store, the service sends a synchronization request to the **DatamgrService**.
The **DatamgrService** reads the data to be synchronized from the application sandbox and sends the data to the **DatamgrService** of the target device based on the **deviceId** of the peer device. Then, the **DatamgrService** writes the data to the RDB of the same application.
### Data Change Notification Mechanism
When data is added, deleted, or modified, a notification is sent to the subscriber. The notifications can be classified into the following types:
- Local data change notification: subscription of the application data changes on the local device. When the data in the local KV store is added, deleted, or modified in the database, a notification is received.
- Distributed data change notification: subscription of the application data changes of other devices in the network. When the data in the local RDB store changes after being synchronized with data from another device in the same network, a notification is received.
## Constraints
- A maximum of 16 distributed RDB stores can be opened simultaneously for an application.
- Each RDB store supports a maximum of eight callbacks for subscription of data change notifications.
- Third-party applications cannot call the distributed APIs that must be specified with the device.
## Available APIs
The following table lists the APIs for cross-device data synchronization of RDB stores. Most of the APIs are executed asynchronously, using a callback or promise to return the result. The following table uses the callback-based APIs as an example. For more information about the APIs, see [RDB Store](../reference/apis/js-apis-data-relationalStore.md).
| API| Description|
| -------- | -------- |
| setDistributedTables(tables: Array&lt;string&gt;, callback: AsyncCallback&lt;void&gt;): void | Sets the distributed tables to be synchronized.|
| sync(mode: SyncMode, predicates: RdbPredicates, callback: AsyncCallback&lt;Array&lt;[string, number]&gt;&gt;): void | Synchronizes data across devices.|
| on(event: 'dataChange', type: SubscribeType, observer: Callback&lt;Array&lt;string&gt;&gt;): void | Subscribes to changes in the distributed data.|
| off(event:'dataChange', type: SubscribeType, observer: Callback&lt;Array&lt;string&gt;&gt;): void | Unsubscribe from changes in the distributed data.|
| obtainDistributedTableName(device: string, table: string, callback: AsyncCallback&lt;string&gt;): void; | Obtains the table name on the specified device based on the local table name.|
| remoteQuery(device: string, table: string, predicates: RdbPredicates, columns: Array&lt;string&gt; , callback: AsyncCallback&lt;ResultSet&gt;): void | Queries data from the RDB store of a remote device based on specified conditions.|
## How to Develop
> **NOTE**
>
> The data on a device can be synchronized only to the devices whose data security labels are not higher than the security level of the device. For details, see [Access Control Mechanism in Cross-Device Synchronization](sync-app-data-across-devices-overview.md#access-control-mechanism-in-cross-device-synchronization).
1. Import the module.
```js
import relationalStore from '@ohos.data.relationalStore';
```
2. Request permissions.
1. Request the **ohos.permission.DISTRIBUTED_DATASYNC** permission. For details, see [Declaring Permissions in the Configuration File](../security/accesstoken-guidelines.md#declaring-permissions-in-the-configuration-file).
2. Display a dialog box to ask authorization from the user when the application is started for the first time. For details, see [Requesting User Authorization](../security/accesstoken-guidelines.md#requesting-user-authorization).
3. Create an RDB store and set a table for distributed synchronization.
```js
const STORE_CONFIG = {
name: 'RdbTest.db', // Database file name.
securityLevel: relationalStore.SecurityLevel.S1 // Database security level.
};
relationalStore.getRdbStore(this.context, STORE_CONFIG, (err, store) => {
store.executeSql('CREATE TABLE IF NOT EXISTS EMPLOYEE (ID INTEGER PRIMARY KEY AUTOINCREMENT, NAME TEXT NOT NULL, AGE INTEGER, SALARY REAL, CODES BLOB)', null, (err) => {
// Set the table for distributed synchronization.
store.setDistributedTables(['EMPLOYEE']);
// Perform related operations.
})
})
```
4. Synchronize data across devices. After **sync()** is called to trigger a synchronization, data is synchronized from the local device to all other devices on the network.
```js
// Construct the predicate object for synchronizing the distributed table.
let predicates = new relationalStore.RdbPredicates('EMPLOYEE');
// Call sync() to synchronize data.
store.sync(relationalStore.SyncMode.SYNC_MODE_PUSH, predicates, (err, result) => {
// Check whether data synchronization is successful.
if (err) {
console.error(`Failed to sync data. Code:${err.code},message:${err.message}`);
return;
}
console.info('Succeeded in syncing data.');
for (let i = 0; i < result.length; i++) {
console.info(`device:${result[i][0]},status:${result[i][1]}`);
}
})
```
5. Subscribe to changes in the distributed data. The data synchronization triggers the **observer** callback registered in **on()**. The input parameter of the callback is the ID of the device whose data changes.
```js
let observer = function storeObserver(devices) {
for (let i = 0; i < devices.length; i++) {
console.info(`The data of device:${devices[i]} has been changed.`);
}
}
try {
// Register an observer to listen for the changes of the distributed data.
// When data in the RDB store changes, the registered callback will be invoked to return the data changes.
store.on('dataChange', relationalStore.SubscribeType.SUBSCRIBE_TYPE_REMOTE, observer);
} catch (err) {
console.error('Failed to register observer. Code:${err.code},message:${err.message}');
}
// You can unsubscribe from the data changes if required.
try {
store.off('dataChange', relationalStore.SubscribeType.SUBSCRIBE_TYPE_REMOTE, observer);
} catch (err) {
console.error('Failed to register observer. Code:${err.code},message:${err.message}');
}
```
6. Query data across devices. If data synchronization is not complete or triggered, an application can call **remoteQuery()** to query data from a remote device.
> **NOTE**
>
> **deviceIds** is obtained by using [devManager.getTrustedDeviceListSync](../reference/apis/js-apis-device-manager.md#gettrusteddevicelistsync). The APIs of the **deviceManager** module are all system interfaces and available only to system applications.
```js
// Obtain device IDs.
import deviceManager from '@ohos.distributedHardware.deviceManager';
deviceManager.createDeviceManager("com.example.appdatamgrverify", (err, manager) => {
if (err) {
console.info(`Failed to create device manager. Code:${err.code},message:${err.message}`);
return;
}
let devices = manager.getTrustedDeviceListSync();
let deviceId = devices[0].deviceId;
// Construct a predicate object for querying the distributed table.
let predicates = new relationalStore.RdbPredicates('EMPLOYEE');
// Query data from the specified remote device and return the query result.
store.remoteQuery(deviceId, 'EMPLOYEE', predicates, ['ID', 'NAME', 'AGE', 'SALARY', 'CODES'],
function (err, resultSet) {
if (err) {
console.error(`Failed to remoteQuery data. Code:${err.code},message:${err.message}`);
return;
}
console.info(`ResultSet column names: ${resultSet.columnNames}, column count: ${resultSet.columnCount}`);
}
)
})
```
# DataShare Development
The **DataShare** module allows an application to manage its own data and share data with other applications. Currently, data can be shared only between applications on the same device.
## Available APIs
**Table 1** APIs of the data provider
|API|Description|
|:------|:------|
|onCreate?(want: Want, callback: AsyncCallback&lt;void&gt;): void|Called to initialize service logic when the data provider application is created, for example, when a database is created.|
|insert?(uri: string, valueBucket: ValuesBucket, callback: AsyncCallback&lt;number&gt;): void|Inserts data into the database.|
|update?(uri: string, predicates: DataSharePredicates, valueBucket: ValuesBucket, callback: AsyncCallback&lt;number&gt;): void|Updates data in the database.|
|query?(uri: string, predicates: DataSharePredicates, columns: Array&lt;string&gt;, callback: AsyncCallback&lt;Object&gt;): void|Queries data from the database.|
|delete?(uri: string, predicates: DataSharePredicates, callback: AsyncCallback&lt;number&gt;): void|Deletes data from the database.|
For details about the data provider APIs, see [DataShareExtensionAbility](../reference/apis/js-apis-application-dataShareExtensionAbility.md).
**Table 2** APIs of the data consumer
| API | Description |
| :----------------------------------------------------------- | :--------------------------------- |
| createDataShareHelper(context: Context, uri: string, callback: AsyncCallback&lt;DataShareHelper&gt;): void | Creates a **DataShareHelper** instance. |
| insert(uri: string, value: ValuesBucket, callback: AsyncCallback&lt;number&gt;): void | Inserts a single data record into the database. |
| update(uri: string, predicates: DataSharePredicates, value: ValuesBucket, callback: AsyncCallback&lt;number&gt;): void | Updates data in the database. |
| query(uri: string, predicates: DataSharePredicates, columns: Array&lt;string&gt;, callback: AsyncCallback&lt;DataShareResultSet&gt;): void | Queries data from the database. |
| delete(uri: string, predicates: DataSharePredicates, callback: AsyncCallback&lt;number&gt;): void | Deletes one or more data records from the database.|
For more information, see [DataShareHelper](../reference/apis/js-apis-data-dataShare.md).
## When to Use
There are two roles in **DataShare**:
- Data provider: adds, deletes, modifies, and queries data, opens files, and shares data.
- Data consumer: accesses the data provided by the provider using **DataShareHelper**.
### Data Provider Application Development (for System Applications Only)
[DataShareExtensionAbility](../reference/apis/js-apis-application-dataShareExtensionAbility.md) provides the following APIs. You can override these APIs as required.
- **onCreate**
Called by the server to initialize service logic when the **DataShare** client connects to the **DataShareExtensionAbility** server.
- **insert**
Inserts data. This API is called when the client requests to insert data.
- **update**
Updates data. This API is called when the client requests to update data.
- **delete**
Deletes data. This API is called when the client requests to delete data.
- **query**
Queries data. This API is called when the client requests to query data.
- **batchInsert**
Batch inserts data. This API is called when the client requests to batch insert data.
- **normalizeUri**
Converts the URI provided by the client to the URI used by the server.
- **denormalizeUri**
Converts the URI used by the server to the initial URI passed by the client.
Before implementing a **DataShare** service, you need to create a **DataShareExtensionAbility** object in the DevEco Studio project as follows:
1. In the **ets** directory of the **Module** project, right-click and choose **New > Directory** to create a directory named **DataShareAbility**.
2. Right-click the **DataShareAbility** directory, and choose **New > TypeScript File** to create a file named **DataShareAbility.ts**.
3. In the **DataShareAbility.ts** file, import **DataShareExtensionAbility** and other dependencies.
```ts
import Extension from '@ohos.application.DataShareExtensionAbility';
import rdb from '@ohos.data.relationalStore';
import fileIo from '@ohos.fileio';
import dataSharePredicates from '@ohos.data.dataSharePredicates';
```
4. Override **DataShareExtensionAbility** APIs based on actual requirements. For example, if the data provider provides only data query, override only **query()**.
5. Implement the data provider services. For example, implement data storage of the data provider by using a database, reading and writing files, or accessing the network.
```ts
const DB_NAME = "DB00.db";
const TBL_NAME = "TBL00";
const DDL_TBL_CREATE = "CREATE TABLE IF NOT EXISTS "
+ TBL_NAME
+ " (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, age INTEGER, isStudent BOOLEAN, Binary BINARY)";
let rdbStore;
let result;
export default class DataShareExtAbility extends Extension {
private rdbStore_;
// Override onCreate().
onCreate(want, callback) {
result = this.context.cacheDir + '/datashare.txt';
// Create an RDB store.
rdb.getRdbStore(this.context, {
name: DB_NAME,
securityLevel: rdb.SecurityLevel.S1
}, function (err, data) {
rdbStore = data;
rdbStore.executeSql(DDL_TBL_CREATE, [], function (err) {
console.log('DataShareExtAbility onCreate, executeSql done err:' + JSON.stringify(err));
});
if (callback) {
callback();
}
});
}
// Override query().
query(uri, predicates, columns, callback) {
if (predicates == null || predicates == undefined) {
console.info('invalid predicates');
}
try {
rdbStore.query(TBL_NAME, predicates, columns, function (err, resultSet) {
if (resultSet != undefined) {
console.info('resultSet.rowCount: ' + resultSet.rowCount);
}
if (callback != undefined) {
callback(err, resultSet);
}
});
} catch (err) {
console.error('error' + err);
}
}
// Override other APIs as required.
// ...
};
```
6. Define **DataShareExtensionAbility** in **module.json5**.
| Field | Description |
| --------- | ------------------------------------------------------------ |
| "name" | Ability name, corresponding to the **ExtensionAbility** class name derived from **Ability**. |
| "type" | Ability type. The value is **dataShare**, indicating the development is based on the **datashare** template. |
| "uri" | URI used for communication. It is the unique identifier for the data consumer to connect to the provider. |
| "visible" | Whether it is visible to other applications. Data sharing is allowed only when the value is **true**. |
**module.json5 example**
```json
"extensionAbilities": [
{
"srcEntrance": "./ets/DataShareExtAbility/DataShareExtAbility.ts",
"name": "DataShareExtAbility",
"icon": "$media:icon",
"description": "$string:description_datashareextability",
"type": "dataShare",
"uri": "datashare://com.samples.datasharetest.DataShare",
"visible": true
}
]
```
### Data Consumer Application Development
1. Import dependencies.
```ts
import UIAbility from '@ohos.app.ability.UIAbility';
import dataShare from '@ohos.data.dataShare';
import dataSharePredicates from '@ohos.data.dataSharePredicates';
```
2. Define the URI string for communicating with the data provider.
```ts
// Different from the URI defined in the module.json5 file, the URI passed in the parameter has an extra slash (/), because there is a DeviceID parameter between the second and the third slash (/).
let dseUri = ("datashare:///com.samples.datasharetest.DataShare");
```
3. Create a **DataShareHelper** instance.
```ts
let dsHelper;
let abilityContext;
export default class EntryAbility extends UIAbility {
onWindowStageCreate(windowStage) {
abilityContext = this.context;
dataShare.createDataShareHelper(abilityContext, dseUri, (err, data)=>{
dsHelper = data;
});
}
}
```
4. Use the APIs provided by **DataShareHelper** to access the services provided by the provider, for example, adding, deleting, modifying, and querying data.
```ts
// Construct a piece of data.
let valuesBucket = { "name": "ZhangSan", "age": 21, "isStudent": false, "Binary": new Uint8Array([1, 2, 3]) };
let updateBucket = { "name": "LiSi", "age": 18, "isStudent": true, "Binary": new Uint8Array([1, 2, 3]) };
let predicates = new dataSharePredicates.DataSharePredicates();
let valArray = ['*'];
// Insert a piece of data.
dsHelper.insert(dseUri, valuesBucket, (err, data) => {
console.log("dsHelper insert result: " + data);
});
// Update data.
dsHelper.update(dseUri, predicates, updateBucket, (err, data) => {
console.log("dsHelper update result: " + data);
});
// Query data.
dsHelper.query(dseUri, predicates, valArray, (err, data) => {
console.log("dsHelper query result: " + data);
});
// Delete data.
dsHelper.delete(dseUri, predicates, (err, data) => {
console.log("dsHelper delete result: " + data);
});
```
# DataShare Overview
## Introduction
The **DataShare** module allows an application to manage its own data and share data with other applications. Currently, data can be shared only between applications on the same device.
**DataShare** must be used together with [DataShareExtensionAbility](../reference/apis/js-apis-application-dataShareExtensionAbility.md).
Data needs to be shared in a wealth of scenarios. For example, contacts, short message service (SMS), and media gallery always needs to be shared. However, certain data, such as accounts and passwords, cannot be shared. Some data, such as SMS messages, can be queried but not modified by other applications. **DataShare** provides a secure data sharing mechanism for applications in a variety of scenarios.
The data provider can directly use the **DataShare** framework to share data with other applications without complex encapsulation. The data consumer only needs to learn and use a set of interfaces because the data access mode does not vary with the data provisioning mode. This greatly reduces the learning time and development difficulty.
## Basic Concepts
Before you get started, familiarize yourself with the following concepts:
- Data provider
The **DataShareExtensionAbility** based on the stage model implements functions, such as selectively adding, deleting, modifying, and querying data, and opening files. It implements services related to cross-application data sharing.
- Data consumer
The data consumer uses **DataShareHelper**, a utility class created by [createDataShareHelper()](../reference/apis/js-apis-data-dataShare.md#datasharecreatedatasharehelper), to access the data provided by the data provider.
- **ValuesBucket**
One or more data records stored in the form of key-value (KV) pairs. The keys are of the string type. The values can be of the number, string, Boolean, or Unit8Array type.
- Result set
A collection of query results. Flexible data access modes are provided for users to obtain data.
- Predicate
Conditions specified for updating, deleting, or querying data in the database.
## Working Principles
**Figure 1** DataShare mechanism
![](figures/en_DataShare.png)
- The **DataShareExtAbility** module, as the data provider, implements services related to data sharing between applications.
- The **DataShareHelper** module, as the data consumer, provides interfaces for accessing data, including adding, deleting, modifying, and querying data.
- The data consumer communicates with the data provider using inter-process communication (IPC). The data provider can be implemented through a database or other data storage.
- The **ResultSet** module is implemented through shared memory. Shared memory stores the result sets, and interfaces are provided to traverse result sets.
## Constraints
- **DataShare** is subject to the limitations on the database used by the data provider. For example, the supported data models, length of the keys and values, and maximum number of databases that can be accessed at a time by each application vary with the database in use.
- The payloads of **ValuesBucket**, predicates, and result sets are restricted by IPC.
# Distributed Data Object Development
## When to Use
The **distributedDataObject** module provides APIs to implement data collaboration of the same application across multiple devices. In addition, the devices that form a Super Device can listen for object status and data changes with each other.
For example, when the data of a distributed data object is added, deleted, or modified for application A on device 1, application A on device 2 can obtain the updated data. In addition, device 2 can listen for data changes and online/offline of the data objects on device 1.
## Available APIs
For details about the APIs, see [Distributed Data Object](../reference/apis/js-apis-data-distributedobject.md).
### Creating a Distributed Data Object Instance
Call **createDistributedObject()** to create a distributed data object instance. You can specify the attributes of the instance in **source**.
**Table 1** API for creating a distributed data object instance
| Bundle Name| API| Description|
| -------- | -------- | -------- |
| ohos.data.distributedDataObject| createDistributedObject(source: object): DistributedObject | Creates a distributed data object instance for data operations.<br>- **source**: attributes of the distributed data object to create.<br>- **DistributedObject**: returns the distributed data object created.|
### Generating a Session ID
Call **genSessionId()** to generate a session ID randomly. The generated session ID can be used to set the session ID of a distributed data object.
**Table 2** API for generating a session ID randomly
| Bundle Name| API| Description|
| -------- | -------- | -------- |
| ohos.data.distributedDataObject| genSessionId(): string | Generates a session ID, which can be used as the session ID of a distributed data object.|
### Setting a Session ID for a Distributed Data Object
Call **setSessionId()** to set a session ID for a distributed data object. The session ID is a unique identifier for one collaboration across devices. The distributed data objects to be synchronized must be associated with the same session ID.
**Table 3** API for setting a session ID
| Class| API| Description|
| -------- | -------- | -------- |
| DistributedDataObject | setSessionId(sessionId?: string): boolean | Sets a session ID for this distributed data object.<br>**sessionId**: ID of the distributed data object on a trusted network. To remove a distributed data object from the network, set this parameter to "" or leave it empty.|
### Observing Data Changes
Call **on()** to subscribe to data changes of a distributed data object. When the data changes, a callback will be invoked to return the data changes. You can use **off()** to unsubscribe from the data changes.
**Table 4** APIs for observing data changes of a distributed data object
| Class| API| Description|
| -------- | -------- | -------- |
| DistributedDataObject| on(type: 'change', callback: Callback<{ sessionId: string, fields: Array&lt;string&gt; }>): void | Subscribes to data changes.|
| DistributedDataObject| off(type: 'change', callback?: Callback<{ sessionId: string, fields: Array&lt;string&gt; }>): void | Unsubscribes from data changes. <br/>**Callback**: callback to unregister. If this parameter is not specified, all data changes of this distributed data object will be unsubscribed from. |
### Observing Online or Offline Status
Call **on()** to subscribe to status changes of a distributed data object. The status can be online or offline. When the status changes, a callback will be invoked to return the status. You can use **off()** to unsubscribe from the status changes.
**Table 5** APIs for observing status changes of a distributed data object
| Class| API| Description|
| -------- | -------- | -------- |
| DistributedDataObject| on(type: 'status', callback: Callback<{ sessionId: string, networkId: string, status: 'online' \| 'offline' }>): void | Subscribes to the status changes of a distributed data object.|
| DistributedDataObject| off(type: 'status', callback?: Callback<{ sessionId: string, deviceId: string, status: 'online' \| 'offline' }>): void | Unsubscribes from status changes of a distributed data object.|
### Saving or Deleting a Distributed Data Object
Call **save()** to save a distributed data object. When the application is active, the saved data will not be released. When the application exits and restarts, the data saved on the device will be restored.
Call **revokeSave()** to delete a distributed data object that is no longer required. If the distributed data object is saved on the local device, **revokeSave()** will delete the data from all trusted devices. If the distributed data object is not saved on the local device, **revokeSave()** will delete the data from the local device.
The saved data will be released in the following cases:
- The data is stored for more than 24 hours.
- The application has been uninstalled.
- Data is successfully restored.
**Table 6** APIs for saving and deleting a distributed data object
| Class| API| Description|
| -------- | -------- | -------- |
| DistributedDataObject | save(deviceId: string): Promise&lt;SaveSuccessResponse&gt; | Saves a distributed data object.|
| DistributedDataObject| revokeSave(): Promise&lt;RevokeSaveSuccessResponse&gt; | Deletes a distributed data object. |
## How to Develop
The following example shows how to implement distributed data object synchronization.
1. Import the @ohos.data.distributedDataObject module to the development environment.
```js
import distributedObject from '@ohos.data.distributedDataObject';
```
2. Apply for the permission.
Add the required permission (FA model) to the **config.json** file.
```json
{
"module": {
"reqPermissions": [
{
"name": "ohos.permission.DISTRIBUTED_DATASYNC"
}
]
}
}
```
For the apps based on the stage model, see [Declaring Permissions](../security/accesstoken-guidelines.md#stage-model).
This permission must also be granted by the user when the application is started for the first time.
```js
// FA model
import featureAbility from '@ohos.ability.featureAbility';
function grantPermission() {
console.info('grantPermission');
let context = featureAbility.getContext();
context.requestPermissionsFromUser(['ohos.permission.DISTRIBUTED_DATASYNC'], 666, function (result) {
console.info(`requestPermissionsFromUser CallBack`);
})
console.info('end grantPermission');
}
grantPermission();
```
```ts
// Stage model
import UIAbility from '@ohos.app.ability.UIAbility';
let context = null;
class EntryAbility extends UIAbility {
onWindowStageCreate(windowStage) {
context = this.context;
}
}
function grantPermission() {
let permissions = ['ohos.permission.DISTRIBUTED_DATASYNC'];
context.requestPermissionsFromUser(permissions).then((data) => {
console.info('success: ${data}');
}).catch((error) => {
console.error('failed: ${error}');
});
}
grantPermission();
```
3. Obtain a distributed data object instance.
```js
let localObject = distributedObject.createDistributedObject({
name: undefined,
age: undefined,
isVis: true,
parent: undefined,
list: undefined
});
let sessionId = distributedObject.genSessionId();
```
4. Add the distributed data object instance to a network for data synchronization. The data objects in the synchronization network include the local and remote objects.
```js
// Local object
let localObject = distributedObject.createDistributedObject({
name: "jack",
age: 18,
isVis: true,
parent: { mother: "jack mom", father: "jack Dad" },
list: [{ mother: "jack mom" }, { father: "jack Dad" }]
});
localObject.setSessionId(sessionId);
// Remote object
let remoteObject = distributedObject.createDistributedObject({
name: undefined,
age: undefined,
isVis: true,
parent: undefined,
list: undefined
});
// After learning that the local device goes online, the remote object synchronizes data. That is, name changes to jack and age to 18.
remoteObject.setSessionId(sessionId);
```
5. Observe the data changes of the distributed data object. You can subscribe to data changes of the remote object. When the data in the remote object changes, a callback will be invoked to return the data changes.
```js
function changeCallback(sessionId, changeData) {
console.info("change" + sessionId);
if (changeData != null && changeData != undefined) {
changeData.forEach(element => {
console.info("changed !" + element + " " + localObject[element]);
});
}
}
// To refresh the page in changeCallback, correctly bind (this) to the changeCallback.
localObject.on("change", this.changeCallback.bind(this));
```
6. Modify attributes of the distributed data object. The object attributes support basic data types (such as number, Boolean, and string) and complex data types (array and nested basic types).
```js
localObject.name = "jack";
localObject.age = 19;
localObject.isVis = false;
localObject.parent = { mother: "jack mom", father: "jack Dad" };
localObject.list = [{ mother: "jack mom" }, { father: "jack Dad" }];
```
> **NOTE**<br>
> For the distributed data object of the complex type, only the root attribute can be modified. The subordinate attributes cannot be modified.
```js
// Supported modification.
localObject.parent = { mother: "mom", father: "dad" };
// Modification not supported.
localObject.parent.mother = "mom";
```
7. Access the distributed data object.<br>Obtain the distributed data object attributes, which are the latest data on the network.
```js
console.info("name " + localObject["name"]);
```
8. Unsubscribe from data changes. You can specify the callback to unregister. If you do not specify the callback, all data change callbacks of the distributed data object will be unregistered.
```js
// Unregister the specified data change callback.
localObject.off("change", changeCallback);
// Unregister all data change callbacks.
localObject.off("change");
```
9. Subscribe to status changes of this distributed data object. A callback will be invoked to report the status change when the target distributed data object goes online or offline.
```js
function statusCallback(sessionId, networkId, status) {
this.response += "status changed " + sessionId + " " + status + " " + networkId;
}
localObject.on("status", this.statusCallback);
```
10. Save a distributed data object and delete it.
```js
// Save a distributed data object.
localObject.save("local").then((result) => {
console.info("save sessionId " + result.sessionId);
console.info("save version " + result.version);
console.info("save deviceId " + result.deviceId);
}, (result) => {
console.info("save local failed.");
});
// Revoke the data saving operation.
localObject.revokeSave().then((result) => {
console.info("revokeSave success.");
}, (result) => {
console.info("revokeSave failed.");
});
```
11. Unsubscribe from the status changes of this distributed data object. You can specify the callback to unregister. If you do not specify the callback, this API unregisters all status change callbacks of this distributed data object.
```js
// Unregister the specified status change callback.
localObject.off("status", this.statusCallback);
// Unregister all status change callbacks.
localObject.off("status");
```
12. Remove the distributed data object from the synchronization network. The data changes on the local object will not be synchronized to the removed distributed data object.
```js
localObject.setSessionId("");
```
# Distributed Data Object Overview
The distributed data object management framework provides object-oriented in-memory data management. It provides basic data management capabilities, such as creating, querying, deleting, and modifying distributed data objects, and observing data and status changes of the distributed data objects. This management framework also provides distributed capabilities to implement data object collaboration for the same application between multiple devices that form a Super Device.
## Basic Concepts
- **Distributed in-memory database**
The distributed in-memory database caches data in the memory so that applications can quickly access data. This database, however, does not store data persistently. If the database is closed, the data is not retained.
- **Distributed data object**
A distributed data object is an encapsulation of the JS object type. Each distributed data object instance creates a data table in the in-memory database. The in-memory databases created for different applications are isolated from each other. Reading data from and writing data to a distributed data object are mapped to the **get** and **put** operations in the corresponding database, respectively.
The distributed data object can be in the following states in its lifecycle:
- **Uninitialized**: The distributed data object is not instantiated or has been destroyed.
- **Local**: The data table is created, but the data cannot be synchronized.
- **Distributed**: The data table is created, and there are at least two online devices with the same session ID. In this case, data can be synchronized across devices. If a device is offline or the session ID is empty, the distributed data object changes to the local state.
## Working Principles
The distributed data objects are encapsulated into JS objects in distributed in-memory databases. This allows the distributed data objects to be operated in the same way as local variables. The system automatically implements cross-device data synchronization.
**Figure 1** Working mechanism
![how-distributedobject-works](figures/how-distributedobject-works.png)
## Constraints
- Data synchronization can be implemented across devices only for the applications with the same **bundleName**.
- Each distributed data object occupies 100 KB to 150 KB of memory. Therefore, you are advised not to create too many distributed data objects.
- The maximum size of a distributed data object is 500 KB.
- For the distributed data object of the complex type, only the root attribute can be modified. The subordinate attributes cannot be modified.
- Only JS APIs are supported.
# Distributed Data Service Development
## When to Use
The Distributed Data Service (DDS) implements synchronization of application data across user devices. When data is added, deleted, or modified for an application on a device, the same application on another device can obtain the updated data. The DDS applies to the distributed gallery, messages, contacts, and file manager.
## Available APIs
For details about the APIs, see [Distributed KV Store](../reference/apis/js-apis-distributedKVStore.md).
**Table 1** APIs provided by the DDS
| API | Description |
| ------------------------------------------------------------ | ------------------------------------------------------------ |
| createKVManager(config: KVManagerConfig): KVManager | Creates a **KvManager** object for database management. |
| getKVStore&lt;T extends KVStore&gt;(storeId: string, options: Options, callback: AsyncCallback&lt;T&gt;): void<br>getKVStore&lt;T extends KVStore&gt;(storeId: string, options: Options): Promise&lt;T&gt; | Creates and obtains a KV store.|
| put(key: string, value: Uint8Array\|string\|number\|boolean, callback: AsyncCallback&lt;void&gt;): void<br>put(key: string, value: Uint8Array\|string\|number\|boolean): Promise&lt;void> | Inserts and updates data. |
| delete(key: string, callback: AsyncCallback&lt;void&gt;): void<br>delete(key: string): Promise&lt;void> | Deletes data. |
| get(key: string, callback: AsyncCallback&lt;Uint8Array\|string\|boolean\|number&gt;): void<br>get(key: string): Promise&lt;Uint8Array\|string\|boolean\|number> | Obtains data. |
| on(event: 'dataChange', type: SubscribeType, observer: Callback&lt;ChangeNotification&gt;): void<br>on(event: 'syncComplete', syncCallback: Callback&lt;Array&lt;[string,number]&gt;&gt;): void | Subscribes to data changes in the KV store. |
| sync(deviceIdList: string[], mode: SyncMode, delayMs?: number): void | Triggers database synchronization in manual mode. |
## How to Develop
The following uses a single KV store as an example to describe the development procedure.
1. Import the distributed data module.
```js
import distributedKVStore from '@ohos.data.distributedKVStore';
```
2. Apply for the required permission if data synchronization is required.
Add the permission required (FA model) in the **config.json** file. The sample code is as follows:
```json
{
"module": {
"reqPermissions": [
{
"name": "ohos.permission.DISTRIBUTED_DATASYNC"
}
]
}
}
```
For the apps based on the stage model, see [Declaring Permissions](../security/accesstoken-guidelines.md#stage-model).
This permission must also be granted by the user when the application is started for the first time. The sample code is as follows:
```js
// FA model
import featureAbility from '@ohos.ability.featureAbility';
function grantPermission() {
console.info('grantPermission');
let context = featureAbility.getContext();
context.requestPermissionsFromUser(['ohos.permission.DISTRIBUTED_DATASYNC'], 666).then((data) => {
console.info('success: ${data}');
}).catch((error) => {
console.error('failed: ${error}');
})
}
grantPermission();
// Stage model
import UIAbility from '@ohos.app.ability.UIAbility';
let context = null;
class EntryAbility extends UIAbility {
onWindowStageCreate(windowStage) {
let context = this.context;
}
}
function grantPermission() {
let permissions = ['ohos.permission.DISTRIBUTED_DATASYNC'];
context.requestPermissionsFromUser(permissions).then((data) => {
console.log('success: ${data}');
}).catch((error) => {
console.error('failed: ${error}');
});
}
grantPermission();
```
3. Create a **KvManager** instance based on the specified **KvManagerConfig** object.
1. Create a **kvManagerConfig** object based on the application context.
2. Create a **KvManager** instance.
The sample code is as follows:
```js
// Obtain the context of the FA model.
import featureAbility from '@ohos.ability.featureAbility';
let context = featureAbility.getContext();
// Obtain the context of the stage model.
import UIAbility from '@ohos.app.ability.UIAbility';
let context = null;
class EntryAbility extends UIAbility {
onWindowStageCreate(windowStage){
context = this.context;
}
}
let kvManager;
try {
const kvManagerConfig = {
bundleName: 'com.example.datamanagertest',
context:context,
}
kvManager = distributedKVStore.createKVManager(kvManagerConfig);
console.log("Created KVManager successfully");
} catch (e) {
console.error(`Failed to create KVManager. Code is ${e.code}, message is ${e.message}`);
}
```
4. Create and obtain a single KV store.
1. Declare the ID of the single KV store to create.
2. Create a single KV store. You are advised to disable automatic synchronization (`autoSync:false`) and call `sync` when a synchronization is required.
The sample code is as follows:
```js
let kvStore;
try {
const options = {
createIfMissing: true,
encrypt: false,
backup: false,
autoSync: false,
kvStoreType: distributedKVStore.KVStoreType.SINGLE_VERSION,
securityLevel: distributedKVStore.SecurityLevel.S1
};
kvManager.getKVStore('storeId', options, function (err, store) {
if (err) {
console.error(`Failed to get KVStore: code is ${err.code}, message is ${err.message}`);
return;
}
console.log('Obtained KVStore successfully');
kvStore = store;
});
} catch (e) {
console.error(`An unexpected error occurred. Code is ${e.code}, message is ${e.message}`);
}
```
> **NOTE**<br>
>
> For data synchronization between networked devices, you are advised to open the distributed KV store during application startup to obtain the database handle. With this database handle (`kvStore` in this example), you can perform operations, such as inserting data into the KV store, without creating the KV store repeatedly during the lifecycle of the handle.
5. Subscribe to changes in the distributed data.
The following is the sample code for subscribing to the data changes of a single KV store:
```js
try{
kvStore.on('dataChange', distributedKVStore.SubscribeType.SUBSCRIBE_TYPE_ALL, function (data) {
console.log(`dataChange callback call data: ${data}`);
});
}catch(e){
console.error(`An unexpected error occured. Code is ${e.code}, message is ${e.message}`);
}
```
6. Write data to the single KV store.
1. Construct the `Key` and `Value` to be written into the single KV store.
2. Write key-value pairs into the single KV store.
The following is the sample code for writing key-value pairs of the string type into the single KV store:
```js
const KEY_TEST_STRING_ELEMENT = 'key_test_string';
const VALUE_TEST_STRING_ELEMENT = 'value-test-string';
try {
kvStore.put(KEY_TEST_STRING_ELEMENT, VALUE_TEST_STRING_ELEMENT, function (err,data) {
if (err != undefined) {
console.error(`Failed to put data. Code is ${err.code}, message is ${err.message}`);
return;
}
console.log("Put data successfully");
});
}catch (e) {
console.error(`An unexpected error occurred. Code is ${e.code}, message is ${e.message}`);
}
```
7. Query data in the single KV store.
1. Construct the `Key` to be queried from the single KV store.
2. Query data from the single KV store.
The following is the sample code for querying data of the string type from the single KV store:
```js
const KEY_TEST_STRING_ELEMENT = 'key_test_string';
const VALUE_TEST_STRING_ELEMENT = 'value-test-string';
try {
kvStore.put(KEY_TEST_STRING_ELEMENT, VALUE_TEST_STRING_ELEMENT, function (err,data) {
if (err != undefined) {
console.error(`Failed to put data. Code is ${err.code}, message is ${err.message}`);
return;
}
console.log("Put data successfully");
kvStore.get(KEY_TEST_STRING_ELEMENT, function (err,data) {
if (err != undefined) {
console.error(`Failed to obtain data. Code is ${err.code}, message is ${err.message}`);
return;
}
console.log(`Obtained data successfully:${data}`);
});
});
}catch (e) {
console.error(`Failed to obtain data. Code is ${e.code}, message is ${e.message}`);
}
```
8. Synchronize data to other devices.
Select the devices in the same network and the synchronization mode to synchronize data.
> **NOTE**<br>
>
> The APIs of the `deviceManager` module are system interfaces.
The following is the example code for synchronizing data in a single KV store:
```js
import deviceManager from '@ohos.distributedHardware.deviceManager';
let devManager;
// Create deviceManager.
deviceManager.createDeviceManager('bundleName', (err, value) => {
if (!err) {
devManager = value;
// deviceIds is obtained by deviceManager by calling getTrustedDeviceListSync().
let deviceIds = [];
if (devManager != null) {
var devices = devManager.getTrustedDeviceListSync();
for (var i = 0; i < devices.length; i++) {
deviceIds[i] = devices[i].deviceId;
}
}
try{
// 1000 indicates that the maximum delay is 1000 ms.
kvStore.sync(deviceIds, distributedKVStore.SyncMode.PUSH_ONLY, 1000);
} catch (e) {
console.error(`An unexpected error occurred. Code is ${e.code}, message is ${e.message}`);
}
}
});
```
# Distributed Data Service Overview
The distributed data service (DDS) implements distributed database collaboration across devices for applications.
Applications save data to distributed databases by calling the DDS APIs. The DDS isolates data of different applications based on a triplet of account, application, and database to ensure secure data access. The DDS synchronizes application data between trusted devices to provide users with consistent data access experience on different devices.
You do not need to care about the implementation of the database locking mechanism.
## Basic Concepts
### KV Data Model
The key-value (KV) data model allows data to be organized, indexed, and stored in KV pairs.
The KV data model is suitable for storing service data that is not related. It provides better read and write performance than the SQL database. The KV data model is widely used in distributed scenarios because it handles database version compatibility issues and data synchronization conflicts easily. The distributed database is based on the KV data model and provides KV-based access interfaces.
### Distributed Database Transaction
Distributed database transactions include local transactions (same as the transactions of traditional databases) and synchronization transactions. Synchronization transactions allow data to be synchronized between devices by local transaction. Synchronization of a local transaction modification either succeeds or fails on all the devices.
### Distributed Database Consistency
In a distributed scenario, cross-device collaboration demands consistent data between the devices in the same network. The data consistency can be classified into the following types:
- **Strong consistency**: When data is inserted, deleted, or modified on a device, other devices in the same network will obtain the latest data immediately.
- **Weak consistency**: When data is added, deleted, or modified on a device, other devices in the same network may or may not obtain the latest data. The data on these devices may be inconsistent after a certain period of time.
- **Eventual consistency**: When data is added, deleted, or modified on a device, other devices in the same network may not obtain the latest data immediately. However, data on these devices will become consistent after a certain period of time.
Strong consistency has high requirements on distributed data management and may be used in distributed server deployment. The DDS supports only the eventual consistency because mobile devices are not always online and the network has no center.
### Distributed Database Synchronization
After discovering and authenticating a device, the underlying communication component notifies the upper-layer application (including the DDS) that the device goes online. The DDS then establishes an encrypted transmission channel to synchronize data between the two devices.
The DDS provides the following synchronization modes:
- **Manual synchronization**: Applications call **sync()** to trigger a synchronization. The list of devices to be synchronized and the synchronization mode must be specified. The synchronization mode can be **PULL_ONLY** (pulling remote data to the local end), **PUSH_ONLY** (pushing local data to the remote end), or **PUSH_PULL** (pushing local data to the remote end and pulling remote data to the local end). The internal interface supports condition-based synchronization. The data that meets the conditions can be synchronized to the remote end.
- **Automatic synchronization**: includes full synchronization and condition-based subscription synchronization. In full synchronization, the distributed database automatically pushes local data to the remote end and pulls remote data to the local end when a device goes online or application data is updated. Applications do not need to call **sync()**. The internal interface supports condition-based subscription synchronization. The data that meets the subscription conditions on the remote end is automatically synchronized to the local end.
### Single KV Store
Data is saved locally in the unit of a single KV entry. Only one entry is saved for each key. Data can be modified only locally and synchronized to remote devices in sequence based on the update time.
### Device KV Store
The device KV store is based on the single KV store. The local device ID is added to the key when KV data is stored in the device KV store. Data can be isolated, managed, and queried by device. However, the data synchronized from remote devices cannot be modified locally.
### Conflict Resolution
A data conflict occurs when multiple devices modify the same data and commit the modification to the database. The last write wins (LWW) is the default conflict resolution policy used for data conflicts. Based on the commit timestamps, the data with a later timestamp is used. Currently, customized conflict resolution policies are not supported.
### Schema-based Database Management and Predicate-based Data Query
A schema is specified when you create or open a single KV store. Based on the schema, the database detects the value format of KV pairs and checks the value structure. Based on the fields in the values, the database implements index creation and predicate-based query.
### Distributed Database Backup
The DDS provides the database backup capability. You can set **backup** to **true** to enable daily backup. If a distributed database is damaged, the DDS deletes the database and restores the most recent data from the backup database. If no backup database is available, the DDS creates one. The DDS can also back up encrypted databases.
## Working Principles
The DDS supports distributed management of application database data in the OpenHarmony system. Data can be synchronized between multiple devices with the same account, delivering a consistent user experience across devices.
The DDS consists of the following:
- **APIs**<br>The DDS provides APIs to create databases, access data, and subscribe to data. The APIs support the KV data model and common data types. They are highly compatible and easy to use, and can be released.
- **Service component**<br>The service component implements management of metadata, permissions, encryption, backup and restore, and multiple users, and completes initialization of the storage component, synchronization component, and communication adaptation layer of the distributed database.
- **Storage component**<br>The storage component implements data access, data reduction, transactions, snapshots, database encryption, data combination, and conflict resolution.
- **Synchronization component**<br>The synchronization component interacts with the storage component and the communication adaptation layer to maintain data consistency between online devices. It synchronizes data generated on the local device to other devices and merges data from other devices into the local device.
- **Communication adaptation layer**<br>The communication adaptation layer calls APIs of the underlying public communication layer to create and connect to communication channels, receive device online and offline messages, update metadata of the connected and disconnected devices, send device online and offline messages to the synchronization component. The synchronization component updates the list of connected devices, and calls the APIs of the communication adaption layer to encapsulate data and send the data to the connected devices.
Applications call the DDS APIs to create, access, and subscribe to distributed databases. The APIs store data to the storage component based on the capabilities provided by the service component. The storage component interacts with the synchronization component to synchronize data. The synchronization component uses the communication adaptation layer to synchronize data to remote devices, which update the data in the storage component and provide the data for applications through service APIs.
**Figure 1** How DDS works
![](figures/en-us_image_0000001183386164.png)
## Constraints
- The DDS supports the KV data model only. It does not support foreign keys or triggers of the relational database.
- The KV data model specifications supported by the DDS are as follows:
- For each record in a device KV store, the key must be less than or equal to 896 bytes and the value be less than 4 MB.
- For each record in a single KV store, the key must be less than or equal to 1 KB and the value be less than 4 MB.
- An application can open a maximum of 16 KV stores simultaneously.
- The data that needs to be synchronized between devices should be stored in distributed databases rather than local databases.
- The DDS does not support customized conflict resolution policies.
- The maximum number of access requests to the KvStore API is 1000 per second and 10000 per minute. The maximum number of access requests to the KvManager API is 50 per second and 500 per minute.
- Blocking operations, such as modifying UI components, are not allowed in the distributed database event callback.
# Preferences Development
> **NOTE**
>
> This feature is supported since API version 9. For the versions earlier than API version 9, use [Lightweight Storage](../reference/apis/js-apis-data-storage.md) APIs.
## When to Use
Preferences are used for storing the data that is frequently used by applications, but not for storing a large amount of data or data frequently changed. The application data is persistently stored on a device in the form of files.
Note that the instance accessed by an application contains all data of the file. The data is always loaded to the memory of the device until the application removes it from the memory. The application can call the **Preferences** APIs to manage data.
## Available APIs
The **Preferences** module provides APIs for processing data in the form of key-value (KV) pairs and supports persistence of the KV pairs when required.
The key is of the string type, and the value can be a number, a string, a Boolean value, or an array of numbers, strings, or Boolean values.
For details about **Preferences** APIs, see [Preferences](../reference/apis/js-apis-data-preferences.md).
### Obtaining a **Preferences** Instance
Obtain a **Preferences** instance for data operations. A **Preferences** instance is obtained after data is read from a specified file and loaded to the instance.
**Table 1** API for obtaining a **Preferences** instance
| Bundle Name | API | Description |
| --------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ |
| ohos.data.preferences | getPreferences(context: Context, name: string): Promise\<Preferences> | Obtains a **Preferences** instance.|
### Processing Data
Call **put()** to add or modify data in a **Preferences** instance.
Call **get()** to read data from a **Preferences** instance.
Call **getAll()** to obtain an **Object** instance that contains all KV pairs in a **Preferences** instance.
Call **delete()** to delete the KV pair of the specified key from the **Preferences** instance.
**Table 2** APIs for processing **Preferences** data
| Class | API | Description |
| ----------- | ---------------------------------------------------------- | ------------------------------------------------------------ |
| Preferences | put(key: string, value: ValueType): Promise\<void> | Writes data to the **Preferences** instance. The value to write can be a number, a string, a Boolean value, or an array of numbers, strings, or Boolean values.|
| Preferences | get(key: string, defValue: ValueType): Promise\<ValueType> | Obtains data from the **Preferences** instance. The value to read can be a number, a string, a Boolean value, or an array of numbers, strings, or Boolean values.|
| Preferences | getAll(): Promise\<Object> | Obtains an **Object** instance that contains all KV pairs in the **Preferences** instance. |
| Preferences | delete(key: string): Promise\<void> | Deletes the KV pair of the specified key from the **Preferences** instance. |
### Storing Data Persistently
Call **flush()** to write the cached data back to its text file for persistent storage.
**Table 4** API for data persistence
| Class | API | Description |
| ----------- | ----------------------- | ------------------------------------------- |
| Preferences | flush(): Promise\<void> | Flushes data from the **Preferences** instance to its file through an asynchronous thread.|
### Observing Data Changes
You can subscribe to data changes. When the value of the subscribed key is changed and saved by **flush()**, a callback will be invoked to return the new data.
**Table 5** APIs for observing **Preferences** changes
| Class | API | Description |
| ----------- | ------------------------------------------------------------ | -------------- |
| Preferences | on(type: 'change', callback: Callback<{ key : string }>): void | Subscribes to data changes.|
| Preferences | off(type: 'change', callback: Callback<{ key : string }>): void | Unsubscribes from data changes. |
### Deleting Data
You can use the following APIs to delete a **Preferences** instance or data file.
**Table 6** APIs for deleting **Preferences**
| Bundle Name | API | Description |
| --------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ |
| ohos.data.preferences | deletePreferences(context: Context, name: string): Promise\<void> | Deletes a **Preferences** instance from the memory and its files from the device.|
| ohos.data.preferences | removePreferencesFromCache(context: Context, name: string): Promise\<void> | Removes a **Preferences** instance from the memory to release memory. |
## How to Develop
1. Import @ohos.data.preferences and related modules to the development environment.
```js
import data_preferences from '@ohos.data.preferences';
```
2. Obtain a **Preferences** instance.
Read the specified file and load its data to the **Preferences** instance for data operations.
FA model:
```js
// Obtain the context.
import featureAbility from '@ohos.ability.featureAbility'
let context = featureAbility.getContext();
let preferences = null;
let promise = data_preferences.getPreferences(context, 'mystore');
promise.then((pref) => {
preferences = pref;
}).catch((err) => {
console.info("Failed to get the preferences.");
})
```
Stage model:
```ts
// Obtain the context.
import UIAbility from '@ohos.app.ability.UIAbility';
let preferences = null;
export default class EntryAbility extends UIAbility {
onWindowStageCreate(windowStage) {
let promise = data_preferences.getPreferences(this.context, 'mystore');
promise.then((pref) => {
preferences = pref;
}).catch((err) => {
console.info("Failed to get the preferences.");
})
}
}
```
3. Write data.
Use **preferences.put()** to write data to the **Preferences** instance.
```js
let putPromise = preferences.put('startup', 'auto');
putPromise.then(() => {
console.info("Put the value of 'startup' successfully.");
}).catch((err) => {
console.info("Failed to put the value of 'startup'. Cause: " + err);
})
```
4. Read data.
Use **preferences.get()** to read data.
```js
let getPromise = preferences.get('startup', 'default');
getPromise.then((value) => {
console.info("The value of 'startup' is " + value);
}).catch((err) => {
console.info("Failed to get the value of 'startup'. Cause: " + err);
})
```
5. Store data persistently.
Use **preferences.flush()** to flush data from the **Preferences** instance to its file.
```js
preferences.flush();
```
6. Observe data changes.
Specify an observer as the callback to subscribe to data changes for an application. When the value of the subscribed key is changed and saved by **flush()**, the observer callback will be invoked to return the new data.
```js
let observer = function (key) {
console.info("The key" + key + " changed.");
}
preferences.on('change', observer);
// The data is changed from 'auto' to 'manual'.
preferences.put('startup', 'manual', function (err) {
if (err) {
console.info("Failed to put the value of 'startup'. Cause: " + err);
return;
}
console.info("Put the value of 'startup' successfully.");
preferences.flush(function (err) {
if (err) {
console.info("Failed to flush data. Cause: " + err);
return;
}
console.info("Flushed data successfully."); // The observer will be called.
})
})
```
7. Delete the specified file.
Use **deletePreferences()** to delete the **Preferences** instance and its persistent file and backup and corrupted files. After the specified files are deleted, the application cannot use that instance to perform any data operation. Otherwise, data inconsistency will be caused. The deleted data and files cannot be restored.
```js
let proDelete = data_preferences.deletePreferences(context, 'mystore');
proDelete.then(() => {
console.info("Deleted data successfully.");
}).catch((err) => {
console.info("Failed to delete data. Cause: " + err);
})
```
# Preferences Overview
Preferences are used to implement quick access and persistence of the data in the `key-value` structure.
After an application obtains a **Preferences** instance, the data in the instance will be cached in the memory for faster access.
The cached data can also be written to a text file for persistent storage. Since file read and write consume system resources, you are advised to minimize the frequency of reading and writing files.
You do not need to care about the implementation of the database locking mechanism.
## Basic Concepts
- **Key-value structure**
A type of data structure. The `Key` is the unique identifier for a piece of data, and the `Value` is the specific data being identified.
- **Non-relational database**
A database not in compliance with the atomicity, consistency, isolation, and durability (ACID) properties of relational data transactions. The data in a non-relational database is independent. The database that organizes data in the `key-value` structure is a non-relational database.
## Working Principles
1. An application can load data from a **Preferences** persistent file to a **Preferences** instance. The system stores the **Preferences** instance in the memory through a static container. Each file of an application or process has only one **Preferences** instance in the memory, till the application removes the instance from the memory or deletes the **Preferences** persistent file.
2. When obtaining a **Preferences** instance, the application can read data from or write data to the instance. The data in the `Preferences` instance can be flushed to its **Preferences** persistent file by calling the **flush()** method.
**Figure 1** Working mechanism
![](figures/preferences.png)
## Constraints
- **Preferences** instances are loaded to the memory. To minimize non-memory overhead, the number of data records stored in a **Preferences** instance cannot exceed 10,000. Delete the instances that are no longer used in a timely manner.
- The `Key` in key-value pairs is of the string type. It cannot be empty or exceed 80 bytes.
- The `Value` of the string type in key-value pairs can be empty, but cannot exceed 8192 bytes if not empty.
# RDB Overview
A relational database (RDB) store manages data based on relational models. With the underlying SQLite database, the RDB store provides a complete mechanism for managing data as in a local database. To satisfy different needs in complicated scenarios, the RDB store offers APIs for performing operations, such as adding, deleting, modifying, and querying data, and supports direct execution of SQL statements. After an application is uninstalled, the related RDB store will be automatically deleted.
You do not need to care about the implementation of the database locking mechanism.
## Basic Concepts
- **RDB store**
A type of database created on the basis of relational models. A RDB store holds data in rows and columns.
- **Predicate**
A representation of the property or feature of a data entity, or the relationship between data entities. Predicates are used to define operation conditions.
- **Result set**
A set of query results used to access data. You can access the required data in a result set in flexible modes.
- **SQLite database**
A lightweight open-source relational database management system that complies with Atomicity, Consistency, Isolation, and Durability (ACID).
## Working Principles
The RDB store provides common operation APIs for external systems. It uses the SQLite as the underlying persistent storage engine, which supports all SQLite database features.
**Figure 1** Working mechanism
![how-rdb-works](figures/how-rdb-works.png)
## Default Settings
- The default RDB logging mode is Write Ahead Log (WAL).
- The default data flushing mode is **FULL** mode.
- The default size of the shared memory used by an OpenHarmony database is 2 MB.
## Constraints
- An RDB store can be connected to a maximum of four connection pools to manage read and write operations.
- To ensure data accuracy, the RDB store supports only one write operation at a time.
# Sharing Data Using DataShareExtensionAbility
## When to Use
If complex services are involved in cross-application data access, you can use **DataShareExtensionAbility** to start the application of the data provider to implement data access.
You need to implement flexible service logics via callbacks of the service provider.
## Working Principles
There are two roles in **DataShare**:
- Data provider: implements operations, such as adding, deleting, modifying, and querying data, and opening a file, using [DataShareExtensionAbility](../reference/apis/js-apis-application-dataShareExtensionAbility.md).
- Data consumer: accesses the data provided by the provider using [createDataShareHelper()](../reference/apis/js-apis-data-dataShare.md#datasharecreatedatasharehelper).
**Figure 1** Data sharing mechanism
![dataShare](figures/dataShare.jpg)
- The **DataShareExtensionAbility** module, as the data provider, implements services related to data sharing between applications.
- The **DataShareHelper** module, as the data consumer, provides APIs for accessing data, including adding, deleting, modifying, and querying data.
- The data consumer communicates with the data provider via inter-process communication (IPC). The data provider can be implemented through a database or other data storage.
- The **ResultSet** module is implemented through shared memory. Shared memory stores the result sets, and interfaces are provided to traverse result sets.
## How to Develop
### Data Provider Application Development (Only for System Applications)
[DataShareExtensionAbility](../reference/apis/js-apis-application-dataShareExtensionAbility.md) provides the following APIs. You can override these APIs as required.
- **onCreate**: called by the server to initialize service logic when the DataShare client connects to the DataShareExtensionAbility server.
- **insert**: called to insert data upon the request of the client. Data insertion must be implemented in this callback on the server.
- **update**: called to update data upon the request of the client. Data update must be implemented in this callback on the server.
- **delete**: called to delete data upon the request of the client. Data deletion must be implemented in this callback on the server.
- **query**: called to query data upon the request of the client. Data query must be implemented in this callback on the server.
- **batchInsert**: called to batch insert data upon the request of the client. Batch data insertion must be implemented in this callback on the server.
- **normalizeUri**: converts the URI provided by the client to the URI used by the server.
- **denormalizeUri**: converts the URI used by the server to the initial URI passed by the client.
Before implementing a **DataShare** service, you need to create a **DataShareExtensionAbility** object in the DevEco Studio project as follows:
1. In the **ets** directory of the **Module** project, right-click and choose **New > Directory** to create a directory named **DataShareExtAbility**.
2. Right-click the **DataShareAbility** directory, and choose **New > TypeScript File** to create a file named **DataShareExtAbility.ts**.
3. Import **@ohos.application.DataShareExtensionAbility** and other dependencies to the **DataShareExtAbility.ts** file, and
override the service implementation as required. For example, if the data provider provides only the data insertion, deletion, and query services, you can override only these APIs.
```js
import Extension from '@ohos.application.DataShareExtensionAbility';
import rdb from '@ohos.data.relationalStore';
import dataSharePredicates from '@ohos.data.dataSharePredicates';
```
4. Implement the data provider services. For example, implement data storage of the data provider by using a database, reading and writing files, or accessing the network.
```js
const DB_NAME = 'DB00.db';
const TBL_NAME = 'TBL00';
const DDL_TBL_CREATE = "CREATE TABLE IF NOT EXISTS "
+ TBL_NAME
+ ' (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, age INTEGER, isStudent BOOLEAN, Binary BINARY)';
let rdbStore;
let result;
export default class DataShareExtAbility extends Extension {
private rdbStore_;
// Override onCreate().
onCreate(want, callback) {
result = this.context.cacheDir + '/datashare.txt';
// Create an RDB store.
rdb.getRdbStore(this.context, {
name: DB_NAME,
securityLevel: rdb.SecurityLevel.S1
}, function (err, data) {
rdbStore = data;
rdbStore.executeSql(DDL_TBL_CREATE, [], (err) => {
console.info(`DataShareExtAbility onCreate, executeSql done err:${err}`);
});
if (callback) {
callback();
}
});
}
// Override query().
query(uri, predicates, columns, callback) {
if (predicates === null || predicates === undefined) {
console.info('invalid predicates');
}
try {
rdbStore.query(TBL_NAME, predicates, columns, (err, resultSet) => {
if (resultSet !== undefined) {
console.info(`resultSet.rowCount:${resultSet.rowCount}`);
}
if (callback !== undefined) {
callback(err, resultSet);
}
});
} catch (err) {
console.error(`Failed to query. Code:${err.code},message:${err.message}`);
}
}
// Override other APIs as required.
};
```
5. Define **DataShareExtensionAbility** in **module.json5**.
**Table 1** Fields in module.json5
| Field| Description| Mandatory|
| -------- | -------- | -------- |
| name | Ability name, corresponding to the **ExtensionAbility** class name derived from **Ability**.| Yes|
| type | Ability type. The value is **dataShare**, indicating the development is based on the **datashare** template.| Yes|
| uri | URI used for communication. It is the unique identifier for the data consumer to connect to the provider.| Yes|
| exported | Whether it is visible to other applications. Data sharing is allowed only when the value is **true**.| Yes|
| readPermission | Permission required for accessing data. If this parameter is not set, the read permission is not verified by default.| No|
| writePermission | Permission required for modifying data. If this parameter is not set, write permission verification is not performed by default.| No|
**module.json5 example**
```json
"extensionAbilities": [
{
"srcEntrance": "./ets/DataShareExtAbility/DataShareExtAbility.ts",
"name": "DataShareExtAbility",
"icon": "$media:icon",
"description": "$string:description_datashareextability",
"type": "dataShare",
"uri": "datashare://com.samples.datasharetest.DataShare",
"exported": true
}
]
```
### Data Consumer Application Development
1. Import the dependencies.
```js
import UIAbility from '@ohos.app.ability.UIAbility';
import dataShare from '@ohos.data.dataShare';
import dataSharePredicates from '@ohos.data.dataSharePredicates';
```
2. Define the URI string for communicating with the data provider.
```js
// Different from the URI defined in the module.json5 file, the URI passed in the parameter has an extra slash (/), because there is a DeviceID parameter between the second and the third slash (/).
let dseUri = ('datashare:///com.samples.datasharetest.DataShare');
```
3. Create a **DataShareHelper** instance.
```js
let dsHelper;
let abilityContext;
export default class EntryAbility extends UIAbility {
onWindowStageCreate(windowStage) {
abilityContext = this.context;
dataShare.createDataShareHelper(abilityContext, dseUri, (err, data) => {
dsHelper = data;
});
}
}
```
4. Use the APIs provided by **DataShareHelper** to access the services provided by the provider, for example, adding, deleting, modifying, and querying data.
```js
// Construct a piece of data.
let valuesBucket = { 'name': 'ZhangSan', 'age': 21, 'isStudent': false, 'Binary': new Uint8Array([1, 2, 3]) };
let updateBucket = { 'name': 'LiSi', 'age': 18, 'isStudent': true, 'Binary': new Uint8Array([1, 2, 3]) };
let predicates = new dataSharePredicates.DataSharePredicates();
let valArray = ['*'];
// Insert a piece of data.
dsHelper.insert(dseUri, valuesBucket, (err, data) => {
console.info(`dsHelper insert result:${data}`);
});
// Update data.
dsHelper.update(dseUri, predicates, updateBucket, (err, data) => {
console.info(`dsHelper update result:${data}`);
});
// Query data.
dsHelper.query(dseUri, predicates, valArray, (err, data) => {
console.info(`dsHelper query result:${data}`);
});
// Delete data.
dsHelper.delete(dseUri, predicates, (err, data) => {
console.info(`dsHelper delete result:${data}`);
});
```
# Data Sharing Through Silent Access
## When to Use
According to big data statistics, in a typical cross-application data access scenario, applications are started nearly 83 times on average in a day.
To reduce the number of application startup times and improve the access speed, OpenHarmony provides the silent access feature, which allows direct access to the database without starting the data provider.
Silent access supports only basic database access. If service processing is required, implement service processing in the data consumer.
If the service processing is complex, use **DataShareExtensionAbility** to start the data provider.
## Working Principles
**Figure 1** Silent access
![silent_dataShare](figures/silent_dataShare.jpg)
- In silent access, **DatamgrService** obtains the access rules configured by the data provider through directory mapping, performs preprocessing based on rules, and accesses the database.
- To use silent access, the URIs must be in the following format:
datashare:///{bundleName}/{moduleName}/{storeName}/{tableName}?Proxy=true
"Proxy=true" means to access data without starting the data provider. If **Proxy** is not set to **true**, the data provider is started.
The **DatamgrService** obtains the data provider application based on **bundleName**, reads the configuration, verifies the permission, and accesses data.
## Constraints
- Currently, only RDB stores support silent access.
- The system supports a maximum of 16 concurrent query operations. Excess query requests need to be queued for processing.
- A proxy cannot be used to create a database. If a database needs to be created, the data provider must be started.
## How to Develop
The URI must be in the following format:
datashare:///{bundleName}/{moduleName}/{storeName}/{tableName}?Proxy=true
For details about the development procedure and implementation, see [Sharing Data Using DataShareExtensionAbility](share-data-by-datashareextensionability.md).
# DataShare Overview
## Function
The **DataShare** module allows an application to share its data with other applications. Currently, data can be shared only between applications on the same device.
Data needs to be shared in a wealth of scenarios. For example, the Contacts, short message service (SMS), and Gallery data always needs to be shared with other applications. However, certain data, such as accounts and passwords, cannot be shared. Some data, such as SMS messages, can be queried but not modified by other applications. **DataShare** provides a secure and convenient sharing mechanism for application data.
The data provider can directly use **DataShare** to share data with other applications without complex encapsulation. The data consumer only needs to use a set of APIs because the DataShare access mode does not vary with the data provision mode. This greatly reduces the learning time and development difficulty.
Data can be shared across applications in either of the following ways:
- Using **DataShareExtensionAbility**
An extension ability is implemented in the HAP to invoke a callback. When the data consumer calls an API, the extension ability of the data provider will be automatically started to invoke the registered callback.
You can use **DataShareExtensionAbility** when the cross-application data access involves service operations other than mere addition, deletion, modification, and query of data in databases.
- Using Silent Access
Database access rules are configured in the HAP. When the data consumer calls an API, the system ability automatically obtains the access rules in the HAP and returns data without starting the data provider.
You can use the silent access feature when the cross-application data access involves only the operations for adding, deleting, modifying, and querying data in databases.
## Basic Concepts
Before developing cross-application data sharing on a device, understand the following concepts:
- Data provider: an application that provides data and implements related services. It is also called the producer or server.
- Data consumer: an application that accesses the data or services provided by the data provider. It is also called the client.
- **ValuesBucket**: a set of data to be inserted. It can be one or more data records in KV pairs. In each KV pair, the key must be of the string type, and the value can be a number, a string, a Boolean value, or an unsigned integer array.
- **ResultSet**: a set of query results. It provides flexible modes for users to obtain various data.
- **Predicates**: an object that specifies the conditions for updating, deleting, or querying data in a database.
## Constraints
- **DataShare** is subject to the limitations on the database used by the data provider. For example, the supported data models, length of the keys and values, and maximum number of databases that can be accessed at a time by each application vary with the database in use.
- The payloads of **ValuesBucket**, **Predicates**, and **ResultSet** are restricted by IPC.
- Currently, **dataShare** supports development based on the stage model only.
# Overview of Distributed Application Data Synchronization
## When to Use
The distributed application data synchronization allows the data of an application to be synchronized with other devices that are connected to form a Virtual Device. This feature enables seamless synchronization, modification, and query of use application data across trusted devices.
For example, when data is added, deleted, or modified for an application on a device, the same application on another device can obtain the updated data. You can use this feature in the distributed Gallery, Notepad, Contacts, and File Manager.
For details about how to subscribe to database change notifications between different applications, see [Sharing Application Data with Other Applications](share-device-data-across-apps-overview.md).
The data storage modes vary depending on the lifecycle of data to be synchronized:
- Temporary data has a short lifecycle and is usually stored in memory. For example, distributed data objects are recommended for process data generated by game applications.
- Persistent data has a long lifecycle and needs to be stored in databases. You can use RDB stores or KV stores based on data characteristics and relationships. For example, RDB stores are recommended for storing Gallery attribute information, such as albums, covers, and images, and KV stores are recommended for storing Gallery image thumbnails.
## Basic Concepts
In a distributed scenario, cross-device collaboration demands consistent data between the devices in the same network.
The data consistency can be classified into the following types:
- Strong consistency: When data is inserted, deleted, or modified on a device, other devices in the same network can obtain the updates eventually, but may not immediately.
- Weak consistency: When data is added, deleted, or modified on a device, other devices in the same network may or may not obtain the updates. The data on these devices may be inconsistent after a certain period of time.
- Eventual consistency: When data is added, deleted, or modified on a device, other devices in the same network may not obtain the updates immediately. However, data on these devices will become consistent after a certain period of time.
Strong consistency has high requirements on distributed data management and may be used in distributed server deployment. Because mobile devices are not always online and there is no central node, the cross-device application data synchronization supports eventual consistency only.
## Access Control Mechanism in Cross-Device Synchronization
In the application data synchronization across devices, data access is controlled based on the device level and [data security label](access-control-by-device-and-data-level.md#data-security-label). In principle, data can be synchronized only to the devices whose data security labels are not higher than the device's security level. The access control matrix is as follows:
|Device Security Level|Data Security Labels of the Synchornizable Device|
|---|---|
|SL1|S1|
|SL2|S1 to S2|
|SL3|S1 to S3|
|SL4|S1 to S4|
|SL5|S1 to S4|
For example, the security level of development boards RK3568 and Hi3516 is SL1. The database with data security label S1 can be synchronized with RK3568 and Hi3516, but the database with database labels S2-S4 cannot.
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册