1# Sharing Data Using DataShareExtensionAbility 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 21 22 23- The **DataShareExtensionAbility** module, as the data provider, implements services related to data sharing between applications. 24 25- The **DataShareHelper** module, as the data consumer, provides APIs for accessing data, including adding, deleting, modifying, and querying data. 26 27- 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. 28 29- The **ResultSet** module is implemented through shared memory. Shared memory stores the result sets, and interfaces are provided to traverse result sets. 30 31 32## How to Develop 33 34 35### Data Provider Application Development (for System Applications Only) 36 37The [DataShareExtensionAbility](../reference/apis-arkdata/js-apis-application-dataShareExtensionAbility-sys.md) provides the following APIs. You can override these APIs as required. 38 39- **onCreate**: called by the server to initialize service logic when the DataShare client connects to the DataShareExtensionAbility server. 40- **insert**: called to insert data upon the request of the client. Data insertion must be implemented in this callback on the server. 41- **update**: called to update data upon the request of the client. Data update must be implemented in this callback on the server. 42- **batchUpdate**: called to update batch data upon the request of the client. Batch data update must be implemented in this callback on the server. 43- **delete**: called to delete data upon the request of the client. Data deletion must be implemented in this callback on the server. 44- **query**: called to query data upon the request of the client. Data query must be implemented in this callback on the server. 45- **batchInsert**: called to batch insert data upon the request of the client. Batch data insertion must be implemented in this callback on the server. 46- **normalizeUri**: converts the URI provided by the client to the URI used by the server. 47- **denormalizeUri**: converts the URI used by the server to the initial URI passed by the client. 48 49Before implementing a **DataShare** service, you need to create a **DataShareExtensionAbility** object in the DevEco Studio project as follows: 50 511. In the **ets** directory of the **Module** project, right-click and choose **New > Directory** to create a directory named **DataShareExtAbility**. 52 532. Right-click the **DataShareAbility** directory, and choose **New > ArkTS File** to create a file named **DataShareExtAbility.ets**. 54 553. 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. 56 57 ```ts 58 import { DataShareExtensionAbility, dataShare, dataSharePredicates, relationalStore, DataShareResultSet } from '@kit.ArkData'; 59 import { Want } from '@kit.AbilityKit'; 60 import { BusinessError } from '@kit.BasicServicesKit' 61 ``` 62 634. 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. 64 65 ```ts 66 const DB_NAME = 'DB00.db'; 67 const TBL_NAME = 'TBL00'; 68 const DDL_TBL_CREATE = "CREATE TABLE IF NOT EXISTS " 69 + TBL_NAME 70 + ' (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, age INTEGER, isStudent BOOLEAN, Binary BINARY)'; 71 72 let rdbStore: relationalStore.RdbStore; 73 let result: string; 74 75 export default class DataShareExtAbility extends DataShareExtensionAbility { 76 // Override onCreate(). 77 onCreate(want: Want, callback: Function) { 78 result = this.context.cacheDir + '/datashare.txt'; 79 // Create an RDB store. 80 relationalStore.getRdbStore(this.context, { 81 name: DB_NAME, 82 securityLevel: relationalStore.SecurityLevel.S3 83 }, (err:BusinessError, data:relationalStore.RdbStore) => { 84 rdbStore = data; 85 rdbStore.executeSql(DDL_TBL_CREATE, [], (err) => { 86 console.info(`DataShareExtAbility onCreate, executeSql done err:${err}`); 87 }); 88 if (callback) { 89 callback(); 90 } 91 }); 92 } 93 94 // Override query(). 95 query(uri: string, predicates: dataSharePredicates.DataSharePredicates, columns: Array<string>, callback: Function) { 96 if (predicates === null || predicates === undefined) { 97 console.info('invalid predicates'); 98 } 99 try { 100 rdbStore.query(TBL_NAME, predicates, columns, (err:BusinessError, resultSet:relationalStore.ResultSet) => { 101 if (resultSet !== undefined) { 102 console.info(`resultSet.rowCount:${resultSet.rowCount}`); 103 } 104 if (callback !== undefined) { 105 callback(err, resultSet); 106 } 107 }); 108 } catch (err) { 109 let code = (err as BusinessError).code; 110 let message = (err as BusinessError).message 111 console.error(`Failed to query. Code:${code},message:${message}`); 112 } 113 } 114 // Override the batchUpdate API. 115 batchUpdate(operations:Record<string, Array<dataShare.UpdateOperation>>, callback:Function) { 116 let recordOps : Record<string, Array<dataShare.UpdateOperation>> = operations; 117 let results : Record<string, Array<number>> = {}; 118 let a = Object.entries(recordOps); 119 for (let i = 0; i < a.length; i++) { 120 let key = a[i][0]; 121 let values = a[i][1]; 122 let result : number[] = []; 123 for (const value of values) { 124 rdbStore.update(TBL_NAME, value.values, value.predicates).then(async (rows) => { 125 console.info('Update row count is ' + rows); 126 result.push(rows); 127 }).catch((err:BusinessError) => { 128 console.info('Update failed, err is ' + JSON.stringify(err)); 129 result.push(-1) 130 }) 131 } 132 results[key] = result; 133 } 134 callback(null, results); 135 } 136 // Override other APIs as required. 137 }; 138 ``` 139 1405. Define **DataShareExtensionAbility** in **module.json5**. 141 142 **Table 1** Fields in module.json5 143 144 | Field| Description| Mandatory| 145 | -------- | -------- | -------- | 146 | name | Ability name, corresponding to the **ExtensionAbility** class name derived from **Ability**.| Yes| 147 | type | Ability type. The value **dataShare** indicates the development is based on the **datashare** template.| Yes| 148 | uri | Unique identifier for the data consumer to access the data provider.| Yes| 149 | exported | Whether it is visible to other applications. Data sharing is allowed only when the value is **true**.| Yes| 150 | readPermission | Permission required for data access. If this parameter is not set, read permission verification is not performed by default.| No| 151 | writePermission | Permission required for data modification. If this parameter is not set, write permission verification is not performed by default.| No| 152 | 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).| 153 154 **module.json5 example** 155 156 ```json 157 "extensionAbilities": [ 158 { 159 "srcEntry": "./ets/DataShareExtAbility/DataShareExtAbility.ets", 160 "name": "DataShareExtAbility", 161 "icon": "$media:icon", 162 "description": "$string:description_datashareextability", 163 "type": "dataShare", 164 "uri": "datashare://com.samples.datasharetest.DataShare", 165 "exported": true, 166 "metadata": [{"name": "ohos.extension.dataShare", "resource": "$profile:data_share_config"}] 167 } 168 ] 169 ``` 170 171 **Table 2** Fields in the data_share_config.json file 172 173 | Field | Description | Mandatory| 174 | ------------------- | ------------------------------------------------------------ | ---- | 175 | 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 | 176 | isSilentProxyEnable | Whether to enable silent access for this ExtensionAbility.<br>The value **true** means to enable silent access, and the value **false** means the opposite.<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 | 177 | launchInfos | Launch information, which includes **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 an an extensionAbility. | No | 178 179 **data_share_config.json Example** 180 181 ```json 182 { 183 "tableConfig":[ 184 { 185 "uri":"*", 186 "crossUserMode":1 187 }, 188 { 189 "uri":"datashare:///com.acts.datasharetest/entry/DB00", 190 "crossUserMode":1 191 }, 192 { 193 "uri":"datashare:///com.acts.datasharetest/entry/DB00/TBL00", 194 "crossUserMode":2 195 } 196 ], 197 "isSilentProxyEnable":true, 198 "launchInfos":[ 199 { 200 "storeId": "test", 201 "tableNames":["test1", "test2"] 202 } 203 ] 204 } 205 ``` 206 207 208### Data Consumer Application Development 209 2101. Import the dependencies. 211 212 ```ts 213 import { UIAbility } from '@kit.AbilityKit'; 214 import { dataShare, dataSharePredicates, DataShareResultSet, ValuesBucket } from '@kit.ArkData'; 215 import { window } from '@kit.ArkUI'; 216 import { BusinessError } from '@kit.BasicServicesKit'; 217 ``` 218 2192. Define the URI string for communicating with the data provider. 220 221 ```ts 222 // 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 (/). 223 let dseUri = ('datashare:///com.samples.datasharetest.DataShare'); 224 ``` 225 2263. Create a **DataShareHelper** instance. 227 228 ```ts 229 let dsHelper: dataShare.DataShareHelper | undefined = undefined; 230 let abilityContext: Context; 231 232 export default class EntryAbility extends UIAbility { 233 onWindowStageCreate(windowStage: window.WindowStage) { 234 abilityContext = this.context; 235 dataShare.createDataShareHelper(abilityContext, dseUri, (err, data) => { 236 dsHelper = data; 237 }); 238 } 239 } 240 ``` 241 2424. Use the APIs provided by **DataShareHelper** to access the services provided by the provider, for example, adding, deleting, modifying, and querying data. 243 244 ```ts 245 // Construct a piece of data. 246 let key1 = 'name'; 247 let key2 = 'age'; 248 let key3 = 'isStudent'; 249 let key4 = 'Binary'; 250 let valueName1 = 'ZhangSan'; 251 let valueName2 = 'LiSi'; 252 let valueAge1 = 21; 253 let valueAge2 = 18; 254 let valueIsStudent1 = false; 255 let valueIsStudent2 = true; 256 let valueBinary = new Uint8Array([1, 2, 3]); 257 let valuesBucket: ValuesBucket = { key1: valueName1, key2: valueAge1, key3: valueIsStudent1, key4: valueBinary }; 258 let updateBucket: ValuesBucket = { key1: valueName2, key2: valueAge2, key3: valueIsStudent2, key4: valueBinary }; 259 let predicates = new dataSharePredicates.DataSharePredicates(); 260 let valArray = ['*']; 261 262 let record: Record<string, Array<dataShare.UpdateOperation>> = {}; 263 let operations1: Array<dataShare.UpdateOperation> = []; 264 let operations2: Array<dataShare.UpdateOperation> = []; 265 let operation1: dataShare.UpdateOperation = { 266 values: valuesBucket, 267 predicates: predicates 268 } 269 operations1.push(operation1); 270 let operation2: dataShare.UpdateOperation = { 271 values: updateBucket, 272 predicates: predicates 273 } 274 operations2.push(operation2); 275 record["uri1"] = operations1; 276 record["uri2"] = operations2; 277 278 if (dsHelper != undefined) { 279 // Insert a piece of data. 280 (dsHelper as dataShare.DataShareHelper).insert(dseUri, valuesBucket, (err:BusinessError, data:number) => { 281 console.info(`dsHelper insert result:${data}`); 282 }); 283 // Update data. 284 (dsHelper as dataShare.DataShareHelper).update(dseUri, predicates, updateBucket, (err:BusinessError, data:number) => { 285 console.info(`dsHelper update result:${data}`); 286 }); 287 // Query data. 288 (dsHelper as dataShare.DataShareHelper).query(dseUri, predicates, valArray, (err:BusinessError, data:DataShareResultSet) => { 289 console.info(`dsHelper query result:${data}`); 290 }); 291 // Delete data. 292 (dsHelper as dataShare.DataShareHelper).delete(dseUri, predicates, (err:BusinessError, data:number) => { 293 console.info(`dsHelper delete result:${data}`); 294 }); 295 // Update data in batches. 296 (dsHelper as dataShare.DataShareHelper).batchUpdate(record).then((data: Record<string, Array<number>>) => { 297 // 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. 298 let a = Object.entries(data); 299 for (let i = 0; i < a.length; i++) { 300 let key = a[i][0]; 301 let values = a[i][1] 302 console.info(`Update uri:${key}`); 303 for (const value of values) { 304 console.info(`Update result:${value}`); 305 } 306 } 307 }); 308 // Close the DataShareHelper instance. 309 (dsHelper as dataShare.DataShareHelper).close(); 310 } 311 ``` 312 313 314 315