• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Sharing Data Using DataShareExtensionAbility (ArkTS) (for System Applications Only)
2
3
4## When to Use
5
6If 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.
7
8You need to implement flexible service logics via callbacks of the service provider.
9
10
11## Working Principles
12
13There are two roles in **DataShare**:
14
15- Data provider: implements operations, such as adding, deleting, modifying, and querying data, and opening a file, using [DataShareExtensionAbility](../reference/apis-arkdata/js-apis-application-dataShareExtensionAbility-sys.md).
16
17- Data consumer: accesses the data provided by the provider using [createDataShareHelper()](../reference/apis-arkdata/js-apis-data-dataShare-sys.md#datasharecreatedatasharehelper).
18
19**Figure 1** Data sharing mechanism
20![dataShare](figures/dataShare.jpg)
21
22- The **DataShareExtensionAbility** module, as the data provider, implements services related to data sharing between applications.
23
24- The **DataShareHelper** module, as the data consumer, provides APIs for accessing data, including adding, deleting, modifying, and querying data.
25
26- 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.
27
28- The **ResultSet** module is implemented through shared memory. Shared memory stores the result sets, and interfaces are provided to traverse result sets.
29
30## How to Develop
31
32
33### Data Provider Application Development (Only for System Applications)
34
35The [DataShareExtensionAbility](../reference/apis-arkdata/js-apis-application-dataShareExtensionAbility-sys.md) provides the following APIs. You can override these APIs as required.
36
37- **onCreate**: called by the server to initialize service logic when the DataShare client connects to the DataShareExtensionAbility server.
38- **insert**: called to insert data upon the request of the client. Data insertion must be implemented in this callback on the server.
39- **update**: called to update data upon the request of the client. Data update must be implemented in this callback on the server.
40- **batchUpdate**: called to update batch data upon the request of the client. Batch data update must be implemented in this callback on the server.
41- **delete**: called to delete data upon the request of the client. Data deletion must be implemented in this callback on the server.
42- **query**: called to query data upon the request of the client. Data query must be implemented in this callback on the server.
43- **batchInsert**: called to batch insert data upon the request of the client. Batch data insertion must be implemented in this callback on the server.
44- **normalizeUri**: converts the URI provided by the client to the URI used by the server.
45- **denormalizeUri**: converts the URI used by the server to the initial URI passed by the client.
46
47Before implementing a **DataShare** service, you need to create a **DataShareExtensionAbility** object in the DevEco Studio project as follows:
48
491. In the **ets** directory of the **Module** project, right-click and choose **New > Directory** to create a directory named **DataShareExtAbility**.
50
512. Right-click the **DataShareExtAbility** directory, and choose **New > ArkTS File** to create a file named **DataShareExtAbility.ets**.
52
533. In the **DataShareExtAbility.ets** file, import the **DataShareExtensionAbility** module. You can override the service implementation as required. For example, if the data provider provides only the insert, delete, and query services, you can override only these APIs and import the dependency modules. If permission verification is required, override the callbacks using [getCallingPid](../reference/apis-ipc-kit/js-apis-rpc.md#getcallingpid), [getCallingUid](../reference/apis-ipc-kit/js-apis-rpc.md#getcallinguid), and [getCallingTokenId](../reference/apis-ipc-kit/js-apis-rpc.md#getcallingtokenid8) provided by the IPC module to obtain the data consumer information for permission verification.
54
55   ```ts
56   import { DataShareExtensionAbility, dataShare, dataSharePredicates, relationalStore, DataShareResultSet } from '@kit.ArkData';
57   import { Want } from '@kit.AbilityKit';
58   import { BusinessError } from '@kit.BasicServicesKit'
59   ```
60
614. Implement the data provider services. For example, implement data storage of the data provider by creating and using a database, reading and writing files, or accessing the network.
62
63   ```ts
64   const DB_NAME = 'DB00.db';
65   const TBL_NAME = 'TBL00';
66   const DDL_TBL_CREATE = "CREATE TABLE IF NOT EXISTS "
67     + TBL_NAME
68     + ' (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, age INTEGER, isStudent BOOLEAN, Binary BINARY)';
69
70   let rdbStore: relationalStore.RdbStore;
71   let result: string;
72
73   export default class DataShareExtAbility extends DataShareExtensionAbility {
74     // Override onCreate().
75     onCreate(want: Want, callback: Function) {
76       result = this.context.cacheDir + '/datashare.txt';
77       // Create an RDB store.
78       relationalStore.getRdbStore(this.context, {
79         name: DB_NAME,
80         securityLevel: relationalStore.SecurityLevel.S3
81       }, (err:BusinessError, data:relationalStore.RdbStore) => {
82         rdbStore = data;
83         rdbStore.executeSql(DDL_TBL_CREATE, [], (err) => {
84           console.info(`DataShareExtAbility onCreate, executeSql done err:${err}`);
85         });
86         if (callback) {
87           callback();
88         }
89       });
90     }
91
92     // Override query().
93     query(uri: string, predicates: dataSharePredicates.DataSharePredicates, columns: Array<string>, callback: Function) {
94       if (predicates === null || predicates === undefined) {
95         console.info('invalid predicates');
96       }
97       try {
98         rdbStore.query(TBL_NAME, predicates, columns, (err:BusinessError, resultSet:relationalStore.ResultSet) => {
99           if (resultSet !== undefined) {
100             console.info(`resultSet.rowCount:${resultSet.rowCount}`);
101           }
102           if (callback !== undefined) {
103             callback(err, resultSet);
104           }
105         });
106       } catch (err) {
107         let code = (err as BusinessError).code;
108         let message = (err as BusinessError).message
109         console.error(`Failed to query. Code:${code},message:${message}`);
110       }
111     }
112     // Override the batchUpdate API.
113     batchUpdate(operations:Record<string, Array<dataShare.UpdateOperation>>, callback:Function) {
114       let recordOps : Record<string, Array<dataShare.UpdateOperation>> = operations;
115       let results : Record<string, Array<number>> = {};
116       let a = Object.entries(recordOps);
117       for (let i = 0; i < a.length; i++) {
118         let key = a[i][0];
119         let values = a[i][1];
120         let result : number[] = [];
121         for (const value of values) {
122           rdbStore.update(TBL_NAME, value.values, value.predicates).then(async (rows) => {
123             console.info('Update row count is ' + rows);
124             result.push(rows);
125           }).catch((err:BusinessError) => {
126             console.info('Update failed, err is ' + JSON.stringify(err));
127             result.push(-1)
128           })
129         }
130         results[key] = result;
131       }
132       callback(null, results);
133     }
134
135     batchInsert(uri: string, valueBuckets:Array<ValuesBucket>, callback:Function) {
136       if (valueBuckets == null || valueBuckets.length == undefined) {
137        return;
138       }
139       let resultNum = valueBuckets.length
140       rdbStore.batchInsert(TBL_NAME, valueBuckets, (err, ret) => {
141        if (callback !== undefined) {
142          callback(err, ret);
143        }
144       });
145     }
146
147     async normalizeUri(uri: string, callback:Function) {
148       let ret = "normalize+" + uri;
149       let err:BusinessError = {
150         message: "message",
151         code: 0,
152         name: 'name'
153       };
154       await callback(err, ret);
155     }
156
157     async denormalizeUri(uri: string, callback:Function) {
158       let ret = "denormalize+" + uri;
159
160       let err:BusinessError = {
161         message: "message",
162         code: 0,
163         name: 'name'
164       };
165       await callback(err, ret);
166     }
167     // Override other APIs as required.
168   };
169   ```
170
1715. Define **DataShareExtensionAbility** in **module.json5**.
172
173     **Table 1** Fields in module.json5
174
175   | Field| Description| Mandatory|
176   | -------- | -------- | -------- |
177   | name | Ability name, corresponding to the **ExtensionAbility** class name derived from **Ability**.| Yes|
178   | type | Ability type. The value **dataShare** indicates that the development is based on the **datashare** template.| Yes|
179   | uri | Unique identifier for the data consumer to access the data provider.| Yes|
180   | exported | Whether it is visible to other applications. Data sharing is allowed only when the value is **true**.| Yes|
181   | readPermission | Permission required for accessing data. If this parameter is not set, read permission verification is not performed by default.<br>**NOTE**: The permission constraints for **DataShareExtensionAbility** are different from that for silent access. It is important to understand the difference and prevent confusion. For details, see [Silent Access via DatamgrService](share-data-by-silent-access.md).| No|
182   | writePermission | Permission required for modifying data. If this parameter is not set, write permission verification is not performed by default.<br>**NOTE**: The permission constraints for **DataShareExtensionAbility** are different from that for silent access. It is important to understand the difference and prevent confusion. For details, see [Silent Access via DatamgrService](share-data-by-silent-access.md).| No|
183   | metadata   | Silent access configuration, which includes the following:<br>**name**: identifies the configuration, which has a fixed value of **ohos.extension.dataShare**.<br>**resource**: has a fixed value of **$profile:data_share_config**, which indicates that the profile name is **data_share_config.json**. | **metadata** is mandatory when the ability launch type is **singleton**. For details about the ability launch type, see **launchType** in the [Internal Structure of the abilities Attribute](../quick-start/module-structure.md#internal-structure-of-the-abilities-attribute).|
184
185   **module.json5 example**
186
187   ```json
188   // The following uses settingsdata as an example.
189   "extensionAbilities": [
190     {
191       "srcEntry": "./ets/DataAbility/DataExtAbility.ets",
192       "name": "DataExtAbility",
193       "icon": "$media:icon",
194       "description": "$string:description_datashareextability",
195       "type": "dataShare",
196       "uri": "datashare://com.ohos.settingsdata.DataAbility",
197       "exported": true,
198       // Configure permissions based on actual situation. The permissions configured here are examples only.
199       "readPermission": "ohos.permission.MANAGE_SECURE_SETTINGS",
200       "writePermission": "ohos.permission.MANAGE_SECURE_SETTINGS",
201       "metadata": [{"name": "ohos.extension.dataShare", "resource": "$profile:data_share_config"}]
202     }
203   ]
204   ```
205
206   **Table 2** Fields in the data_share_config.json file
207
208   | Field           | Description                                                    | Mandatory|
209   | ------------------- | ------------------------------------------------------------ | ---- |
210   | tableConfig         | Configuration label, which includes **uri** and **crossUserMode**.<br>- **uri**: specifies the range for which the configuration takes effect. The URI supports the following formats in descending order by priority:<br> 1. *****: indicates all databases and tables.<br> 2. **datashare:///{*bundleName*}/{*moduleName*}/{*storeName*}**: specifies a database.<br> 3. **datashare:///{*bundleName*}/{*moduleName*}/{*storeName*}/{*tableName*}**: specifies a table.<br>If URIs of different formats are configured, only the URI with the higher priority takes effect.<br>- **crossUserMode**: Whether to share data between multiple users.<br>The value **1** means to share data between multiple users, and the value **2** means the opposite.| Yes  |
211   | isSilentProxyEnable | Whether to enable silent access for this ExtensionAbility.<br>**false**: Silent access is disabled.<br>**true**: Silent access is enabled.<br>The default value is **true**.<br>If an application has multiple ExtensionAbilities and this field is set to **false** for one of them, silent access is disabled for the application.<br>If the data provider has called **enableSilentProxy** or **disableSilentProxy**, silent access is enabled or disabled based on the API settings. Otherwise, the setting here takes effect.| No  |
212   | launchInfos         | Information including **storeId** and **tableNames**.<br>If the data in a table involved in the configuration changes, an extensionAbility will be started based on the URI in **extensionAbilities**. You need to set this parameter only when the service needs to start an extensionAbility to process data that is not actively changed by the service.<br>- **storeId**: database name, excluding the file name extension. For example, if the database name is **test.db**, set this parameter to **test**.<br>- **tableNames**: names of the database tables. Any change in a table will start **extension**.| No  |
213
214   **data_share_config.json Example**
215
216   ```json
217   {
218       "tableConfig":[
219           {
220               "uri":"*",
221               "crossUserMode":1
222           },
223           {
224               "uri":"datashare:///com.ohos.settingsdata/entry/DB00",
225               "crossUserMode":1
226           },
227           {
228               "uri":"datashare:///com.acts.datasharetest/entry/DB00/TBL00",
229               "crossUserMode":2
230           }
231       ],
232       "isSilentProxyEnable":true,
233       "launchInfos":[
234           {
235               "storeId": "test",
236               "tableNames":["test1", "test2"]
237           }
238       ]
239   }
240   ```
241
242
243### Data Consumer Application Development
244
2451. Import the dependencies.
246
247   ```ts
248   import { UIAbility } from '@kit.AbilityKit';
249   import { dataShare, dataSharePredicates, DataShareResultSet, ValuesBucket } from '@kit.ArkData';
250   import { window } from '@kit.ArkUI';
251   import { BusinessError } from '@kit.BasicServicesKit';
252   ```
253
2542. Define the URI string for communicating with the data provider.<br>The URI is the identifier of the context data provider in set in the configuration file. It can be added with suffix parameters to set the access target. The suffix parameters must start with a question mark (?).<br> - Currently, only the **user** parameter is supported.<br> - The value of **user** must be an integer. It indicates the user ID of the data provider. If It is not specified, the user ID of the data consumer is used. For details about the definition of **user** and how to obtain it, see [user](../reference/apis-basic-services-kit/js-apis-osAccount.md#getactivatedosaccountlocalids9).<br> - Currently, the data consumer in cross-user access must have the ohos.permission.INTERACT_ACROSS_LOCAL_ACCOUNTS permission. Currently, cross-user access supports the add, delete, modify, and query operations, and does not support subscription notification.
255
256   ```ts
257   // 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 (/).
258   let dseUri = ('datashare:///com.ohos.settingsdata.DataAbility');
259   ```
260
2613. Create a **DataShareHelper** instance.
262
263   ```ts
264   let dsHelper: dataShare.DataShareHelper | undefined = undefined;
265   let abilityContext: Context;
266
267   export default class EntryAbility extends UIAbility {
268     onWindowStageCreate(windowStage: window.WindowStage) {
269       abilityContext = this.context;
270       dataShare.createDataShareHelper(abilityContext, dseUri, (err, data) => {
271         dsHelper = data;
272       });
273     }
274   }
275   ```
276
2774. Use the APIs provided by **DataShareHelper** to access the services provided by the provider, for example, adding, deleting, modifying, and querying data.
278
279   ```ts
280   // Construct a piece of data.
281   let key1 = 'name';
282   let key2 = 'age';
283   let key3 = 'isStudent';
284   let key4 = 'Binary';
285   let valueName1 = 'ZhangSan';
286   let valueName2 = 'LiSi';
287   let valueAge1 = 21;
288   let valueAge2 = 18;
289   let valueIsStudent1 = false;
290   let valueIsStudent2 = true;
291   let valueBinary = new Uint8Array([1, 2, 3]);
292   let valuesBucket: ValuesBucket = { key1: valueName1, key2: valueAge1, key3: valueIsStudent1, key4: valueBinary };
293   let updateBucket: ValuesBucket = { key1: valueName2, key2: valueAge2, key3: valueIsStudent2, key4: valueBinary };
294   let predicates = new dataSharePredicates.DataSharePredicates();
295   let valArray = ['*'];
296
297   let record: Record<string, Array<dataShare.UpdateOperation>> = {};
298   let operations1: Array<dataShare.UpdateOperation> = [];
299   let operations2: Array<dataShare.UpdateOperation> = [];
300   let operation1: dataShare.UpdateOperation = {
301     values: valuesBucket,
302     predicates: predicates
303   }
304   operations1.push(operation1);
305   let operation2: dataShare.UpdateOperation = {
306     values: updateBucket,
307     predicates: predicates
308   }
309   operations2.push(operation2);
310   record["uri1"] = operations1;
311   record["uri2"] = operations2;
312
313   if (dsHelper != undefined) {
314     // Insert a piece of data.
315     (dsHelper as dataShare.DataShareHelper).insert(dseUri, valuesBucket, (err:BusinessError, data:number) => {
316       console.info(`dsHelper insert result:${data}`);
317     });
318     // Update data.
319     (dsHelper as dataShare.DataShareHelper).update(dseUri, predicates, updateBucket, (err:BusinessError, data:number) => {
320       console.info(`dsHelper update result:${data}`);
321     });
322     // Query data.
323     (dsHelper as dataShare.DataShareHelper).query(dseUri, predicates, valArray, (err:BusinessError, data:DataShareResultSet) => {
324       console.info(`dsHelper query result:${data}`);
325     });
326     // Delete data.
327     (dsHelper as dataShare.DataShareHelper).delete(dseUri, predicates, (err:BusinessError, data:number) => {
328       console.info(`dsHelper delete result:${data}`);
329     });
330     // Update data in batches.
331     (dsHelper as dataShare.DataShareHelper).batchUpdate(record).then((data: Record<string, Array<number>>) => {
332        // Traverse data to obtain the update result of each data record. value indicates the number of data records that are successfully updated. If value is less than 0, the update fails.
333        let a = Object.entries(data);
334        for (let i = 0; i < a.length; i++) {
335          let key = a[i][0];
336          let values = a[i][1]
337          console.info(`Update uri:${key}`);
338          for (const value of values) {
339            console.info(`Update result:${value}`);
340          }
341        }
342      });
343     // Close the DataShareHelper instance.
344     (dsHelper as dataShare.DataShareHelper).close();
345   }
346   ```
347