1# 通过数据管理服务实现数据共享静默访问 2 3 4## 场景介绍 5 6典型跨应用访问数据的用户场景下,数据提供方会存在多次被拉起的情况。 7 8为了降低数据提供方拉起次数,提高访问速度,OpenHarmony提供了一种不拉起数据提供方直接访问数据库的方式,即静默数据访问。 9 10静默数据访问通过数据管理服务进行数据的访问和修改,无需拉起数据提供方。 11 12数据管理服务仅支持数据库的基本访问或数据托管,如果有业务处理,需要将业务处理封装成接口,给数据访问方调用。 13 14如果业务过于复杂,无法放到数据访问方,建议通过[DataShareExtensionAbility](../reference/apis-arkdata/js-apis-application-dataShareExtensionAbility-sys.md)拉起数据提供方实现功能。 15 16 17## 运作机制 18 19可以通过数据管理服务进行代理访问的数据分为以下三种: 20 21- 持久化数据:归属于数据提供方的数据库,这类数据存储于数据提供方的沙箱,可以在数据提供方中通过声明的方式进行共享,按表为粒度配置为可以被其他应用访问的数据表。 22 23 24- 过程数据:托管在数据管理服务上的过程数据,这类数据存储于数据管理服务的沙箱,格式为json或byte数据,无人订阅10天后自动删除。 25 26 27- 动态数据:托管在设备上的动态数据,这类数据存储于内存中,设备重启之后自动删除。只限于调用enableSilentProxy和disableSilentProxy接口设置的数据。 28 29 30| 数据类型 | 存储位置 | 数据格式 | 有效期 | 适用场景 | 31| ----- | --------- | ----------- | ------------ | --------------------------------- | 32| 持久化数据 | 数据提供方的沙箱 | 数据库中的数据表 | 永久存储 | 适用于数据格式类似关系型数据库的相关场景,如日程,会议等。 | 33| 过程数据 | 数据管理服务的沙箱 | json或byte数据 | 无人订阅10天后自动删除 | 适用于数据有时效性且数据格式较简单的相关场景,如步数,天气,心率等。 | 34| 动态数据 | 数据管理服务的内存 | key-value数据 | 设备重启之后自动删除 | 适用于动态关闭/打开静默访问通道的场景。例如:升级过程中为了保证数据正确性可以动态关闭静默访问,升级结束后再调用相关接口打开静默访问。调用接口生成的开启关闭状态,设备重启之后会清除。只限于调用enableSilentProxy和disableSilentProxy接口设置的数据。 | 35 36 37 38图1 静默数据访问视图 39 40 41 42- 和跨应用数据共享方式不同的是,静默数据访问借助数据管理服务通过目录映射方式直接读取数据提供方的配置,按规则进行预处理后,并访问数据库。 43 44- 数据访问方的URI需严格按照如下格式:`datashareproxy://{bundleName}/{dataPath}` 45 46 数据管理服务会读取对应bundleName作为数据提供方应用,读取配置,进行权限校验并访问对应数据。 47 48 dataPath为数据标识,可以自行定义,在同一个数据提供方应用中需要保持唯一。 49 50- URI还支持添加其他参数来设置具体的访问方式或访问对象,URI添加参数需严格遵循格式:`datashareproxy://{bundleName}/{dataPath}?{arg1}&{arg2}`,不符合规范的URI参数不生效。 51 52 其中以"?"符号开始参数,以"&"符号连接参数,连续的多个符号会被视为一个。当前仅支持“Proxy”、“appIndex”以及“user”参数。当使用多个"?"符开始参数时,"?"后的参数需是"Proxy"参数,否则该参数不生效。 53 54 - "Proxy"仅支持设置为true或false,true表示数据访问方采用静默数据访问方式,false则表示数据访问方采用非静默数据访问方式。 55 56 - "appIndex"仅支持设置为整型,表示应用包的分身索引,从1开始支持,仅在分身应用中生效。appIndex的定义及获取参照[BundleInfo](../reference/apis-ability-kit/js-apis-bundleManager-bundleInfo.md)。appIndex为0,或不填写时,访问数据提供者的应用本体。 57 58 目前访问应用分身仅支持静默访问方式下,不支持非静默访问方式。故需要设置访问应用分身URI和参数时,请注意同步设置"Proxy"参数和"appIndex"参数。例如“datashareproxy://{bundleName}/{dataPath}?Proxy=true&appIndex=1”,表示将在数据访问方会在静默访问方式下访问应用的第一个分身。 59 60 - "user"仅支持设置为整型,表示要访问的数据提供方的用户ID。user的定义及获取参照[user](../reference/apis-basic-services-kit/js-apis-osAccount.md#getactivatedosaccountlocalids9)。user不填写时,默认为数据访问方所在的用户ID。 61 62 目前跨用户访问仅支持主空间和隐私空间之间的访问,且需要数据访问方配有跨用户访问权限ohos.permission.INTERACT_ACROSS_LOCAL_ACCOUNTS才可成功访问。 63 64## 约束与限制 65 66- 目前持久化数据中仅关系型数据库支持静默数据访问方式。 67- 整个系统最多同时并发16路查询,有多出来的查询请求需要排队处理。 68- 持久化数据不支持代理创建数据库,如果需要创建数据库,需要拉起数据提供方。 69- 数据提供方如果是normal级别签名的应用,配置的数据读写权限必须为system_basic及以上权限。 70 71 72## 接口说明 73 74以下是静默数据访问的相关接口,大部分为异步接口。异步接口均有callback和Promise两种返回形式,下表均以callback形式为例,更多接口及使用方式请见[数据共享](../reference/apis-arkdata/js-apis-data-dataShare-sys.md)。 75 76### 通用接口 77 78| 接口名称 | 描述 | 79| ---------------------------------------- | -------------------- | 80| createDataShareHelper(context: Context, uri: string, options: DataShareHelperOptions, callback: AsyncCallback<DataShareHelper>): void | 创建DataShareHelper实例。 | 81 82### 持久化数据 83 84| 接口名称 | 描述 | 85| ---------------------------------------- | -------------------- | 86| insert(uri: string, value: ValuesBucket, callback: AsyncCallback<number>): void | 向目标表中插入一行数据。 | 87| delete(uri: string, predicates: dataSharePredicates.DataSharePredicates, callback: AsyncCallback<number>): void | 从数据库中删除一条或多条数据记录。 | 88| query(uri: string, predicates: dataSharePredicates.DataSharePredicates, columns: Array<string>, callback: AsyncCallback<DataShareResultSet>): void | 查询数据库中的数据。 | 89| update(uri: string, predicates: dataSharePredicates.DataSharePredicates, value: ValuesBucket, callback: AsyncCallback<number>): void | 更新数据库中的数据记录。 | 90| addTemplate(uri: string, subscriberId: string, template: Template): void | 添加一个指定订阅者的数据模板。 | 91| on(type: 'rdbDataChange', uris: Array<string>, templateId: TemplateId, callback: AsyncCallback<RdbDataChangeNode>): Array<OperationResult> | 订阅指定URI和模板对应的数据变更事件。 | 92 93### 过程数据 94 95| 接口名称 | 描述 | 96| ---------------------------------------- | ------------------ | 97| publish(data: Array<PublishedItem>, bundleName: string, version: number, callback: AsyncCallback<Array<OperationResult>>): void | 发布数据,将数据托管至数据管理服务。 | 98| on(type: 'publishedDataChange', uris: Array<string>, subscriberId: string, callback: AsyncCallback<PublishedDataChangeNode>): Array<OperationResult> | 订阅已发布数据的数据变更通知。 | 99 100### 动态数据 101 102| 接口名称 | 描述 | 103| ---------------------------------------- | ------------------ | 104| enableSilentProxy(context: Context, uri?: string): Promise<void> | 数据提供方动态开启静默访问。<br />当访问方通过静默访问调用DataShare相关接口的时候,校验静默访问的开关状态。<br />如果静默访问的是开启的,DataShare相关接口会执行原逻辑。 | 105| disableSilentProxy(context: Context, uri?: string): Promise<void> | 数据提供方来动态关闭静默访问。<br />当访问方通过静默访问调用DataShare相关接口的时候,校验静默访问的开关状态。<br />如果静默访问的是关闭的,DataShare相关接口接口将会直接返回。 | 106 107 108 109## 持久化数据实现说明 110 111首先,以共享一个关系型数据库为例,说明开发步骤。 112 113### 数据提供方应用的开发 114 1151. 数据提供方需要在module.json5中的proxyData节点定义要共享的表的标识,读写权限和基本信息, 配置方法可考参考[配置文件](../quick-start/module-configuration-file.md)。 116 117 **表1** module.json5中proxyData节点对应的属性字段 118 119 | 属性名称 | 备注说明 | 必填 | 120 | ----------------------- | ---------------------------------------- | ---- | 121 | uri | 数据使用的URI,是跨应用数据访问的唯一标识。 | 是 | 122 | requiredReadPermission | 标识从该数据代理读取数据时所需要的权限,不配置默认不允许其他APP访问数据。支持权限可参考[权限列表](../security/AccessToken/app-permissions.md)。<br>注意:当前静默访问的权限约束方式与DataShareExtensionAbility的权限约束方式不同,请注意区分,切勿混淆,具体可参考[DataShareExtensionAbility章节](share-data-by-datashareextensionability.md)。 | 否 | 123 | requiredWritePermission | 标识从该数据代理修改数据时所需要的权限,不配置默认不允许其他APP修改数据。支持权限可参考[权限列表](../security/AccessToken/app-permissions.md)。<br>注意:当前静默访问的权限约束方式与DataShareExtensionAbility的权限约束方式不同,请注意区分,切勿混淆,具体可参考[DataShareExtensionAbility章节](share-data-by-datashareextensionability.md)。 | 否 | 124 | metadata | 数据源的信息,包含name和resource字段。<br /> name类型固定为"dataProperties",是配置的唯一标识。 <br /> resource类型固定为"$profile:{fileName}",表示配置文件的名称为{fileName}.json。 | 是 | 125 126 一个URI所能访问的范围为一张数据表,因此请确认表中的所有数据适用相同的权限范围,推荐不同范围的数据分开使用多张表存储,配置各自的权限约束,做好表粒度的数据隔离。针对风险等级高的数据,建议配置白名单来限制数据访问方,具体请参考下方my_config.json示例中的allowLists字段。 127 128 **module.json5配置样例:** 129 130 ```json 131 // 以下配置以settingsdata为例,应用需根据实际情况配置各个字段 132 "proxyData": [ 133 { 134 "uri": "datashareproxy://com.ohos.settingsdata/entry/settingsdata/USER_SETTINGSDATA_SECURE", 135 // 实际请按照应用具体场景需要的安全权限配置,如配置应用自定义权限、系统权限或用户授权权限,当前权限仅为示例 136 "requiredReadPermission": "ohos.permission.MANAGE_SECURE_SETTINGS", 137 "requiredWritePermission": "ohos.permission.MANAGE_SECURE_SETTINGS", 138 "metadata": { 139 "name": "dataProperties", 140 "resource": "$profile:my_config" 141 } 142 } 143 ] 144 ``` 145 **表2** my_config.json对应属性字段 146 147 | 属性名称 | 备注说明 | 必填 | 148 | ----- | ---------------------------------------- | ---- | 149 | path | 指定数据源路径,目前支持关系型数据库,配置为库名/表名。 | 是 | 150 | type | 标识数据库类型,目前支持配置为rdb,表示关系型数据库。 | 是 | 151 | scope | 数据库所在范围。<br>1.module表示数据库位于本模块下。<br>2.application表示数据库位于本应用下。 | 否 | 152 | allowLists | 包括appIdentifier和onlyMain。<br>allowLists中配置被允许访问的应用列表,最多配置256个授权信息。在跨应用数据访问时通过该配置校验数据访问方是否在数据提供方配置的列表内,若不在则拒绝访问。若无allowLists配置则不做白名单校验。不管是否配置allowLists,在[表1](#数据提供方应用的开发)中的读写权限依然会被正常校验。<br>**-appIdentifier:** 字符串,应用的唯一标识,由云端统一分配。数据提供方自行向访问方获取。<br>appIdentifier信息可参考[应用包信息](../reference/apis-ability-kit/js-apis-bundleManager-bundleInfo.md#signatureinfo)。 <br>**-onlyMain:** 布尔值,控制是否仅支持主应用。true: 只允许主应用访问,分身应用不可访问。false: 主应用和分身应用均可访问。该功能仅支持静默访问。 | 否 | 153 154 **my_config.json配置样例** 155 156 ```json 157 { 158 "path": "DB00/TBL00", 159 "type": "rdb", 160 "scope": "application", 161 "allowLists":[ 162 {"appIdentifier": "appIdentifier1", "onlyMain": false}, 163 {"appIdentifier": "appIdentifier2", "onlyMain": true} 164 ] 165 } 166 ``` 167 168### 数据访问方应用的开发 169 170 1711. 导入基础依赖包。 172 173 ```ts 174 import { dataShare, dataSharePredicates, ValuesBucket } from '@kit.ArkData'; 175 import { UIAbility } from '@kit.AbilityKit'; 176 import { window } from '@kit.ArkUI'; 177 import { BusinessError } from '@kit.BasicServicesKit' 178 ``` 179 1802. 定义与数据提供方通信的URI字符串。 181 182 ```ts 183 let dseUri = ('datashareproxy://com.ohos.settingsdata/entry/settingsdata/USER_SETTINGSDATA_SECURE'); 184 ``` 185 1863. 创建工具接口类对象。 187 188 ```ts 189 let dsHelper: dataShare.DataShareHelper | undefined = undefined; 190 let abilityContext: Context; 191 192 export default class EntryAbility extends UIAbility { 193 onWindowStageCreate(windowStage: window.WindowStage) { 194 abilityContext = this.context; 195 dataShare.createDataShareHelper(abilityContext, dseUri, { 196 isProxy: true 197 }, (err, data) => { 198 dsHelper = data; 199 }); 200 } 201 } 202 ``` 203 2044. 获取到接口类对象后,便可利用其提供的接口访问提供方提供的服务,如进行数据的增、删、改、查等。 205 206 ```ts 207 // 构建一条数据 208 let key1 = 'name'; 209 let key2 = 'age'; 210 let key3 = 'isStudent'; 211 let key4 = 'Binary'; 212 let valueName1 = 'ZhangSan'; 213 let valueName2 = 'LiSi'; 214 let valueAge1 = 21; 215 let valueAge2 = 18; 216 let valueIsStudent1 = false; 217 let valueIsStudent2 = true; 218 let valueBinary = new Uint8Array([1, 2, 3]); 219 let valuesBucket: ValuesBucket = { key1: valueName1, key2: valueAge1, key3: valueIsStudent1, key4: valueBinary }; 220 let updateBucket: ValuesBucket = { key1: valueName2, key2: valueAge2, key3: valueIsStudent2, key4: valueBinary }; 221 let predicates = new dataSharePredicates.DataSharePredicates(); 222 let valArray = ['*']; 223 if (dsHelper != undefined) { 224 // 插入一条数据 225 (dsHelper as dataShare.DataShareHelper).insert(dseUri, valuesBucket, (err, data) => { 226 console.info(`dsHelper insert result:${data}`); 227 }); 228 // 更新数据 229 (dsHelper as dataShare.DataShareHelper).update(dseUri, predicates, updateBucket, (err, data) => { 230 console.info(`dsHelper update result:${data}`); 231 }); 232 // 查询数据 233 (dsHelper as dataShare.DataShareHelper).query(dseUri, predicates, valArray, (err, data) => { 234 console.info(`dsHelper query result:${data}`); 235 }); 236 // 删除指定的数据 237 (dsHelper as dataShare.DataShareHelper).delete(dseUri, predicates, (err, data) => { 238 console.info(`dsHelper delete result:${data}`); 239 }); 240 } 241 ``` 242 2435. 对指定的数据进行订阅。 244 245 ```ts 246 function onCallback(err: BusinessError, node: dataShare.RdbDataChangeNode) { 247 console.info("uri " + JSON.stringify(node.uri)); 248 console.info("templateId " + JSON.stringify(node.templateId)); 249 console.info("data length " + node.data.length); 250 for (let i = 0; i < node.data.length; i++) { 251 console.info("data " + node.data[i]); 252 } 253 } 254 255 let key21: string = "p1"; 256 let value21: string = "select * from TBL00"; 257 let key22: string = "p2"; 258 let value22: string = "select name from TBL00"; 259 let template: dataShare.Template = { 260 predicates: { 261 key21: value21, 262 key22: value22, 263 }, 264 scheduler: "" 265 } 266 if(dsHelper != undefined) 267 { 268 (dsHelper as dataShare.DataShareHelper).addTemplate(dseUri, "111", template); 269 } 270 let templateId: dataShare.TemplateId = { 271 subscriberId: "111", 272 bundleNameOfOwner: "com.ohos.settingsdata" 273 } 274 if(dsHelper != undefined) { 275 // 使用数据管理服务修改数据时触发onCallback回调,回调内容是template中的规则查到的数据 276 let result: Array<dataShare.OperationResult> = (dsHelper as dataShare.DataShareHelper).on("rdbDataChange", [dseUri], templateId, onCallback); 277 } 278 ``` 279 280## 过程数据实现说明 281 282以托管一份过程数据为例,说明开发步骤。 283 284### 数据提供方应用的开发(可选) 285 286数据提供方需要在module.json5中的proxyData节点定义过程数据的标识,读写权限和基本信息, 配置方法可考参考[配置文件](../quick-start/module-configuration-file.md)。 287 288> 注意: 289> 290> - 该步骤为可选,可以不对module.json5中的proxyData进行配置。 291> - 不配置proxyData时,托管数据不允许其他应用访问。 292> - 不配置proxyData时,数据标识可以为简写,发布、订阅、查询数据可以使用简写的数据标识,如weather,可以不用全写为datashareproxy://com.acts.ohos.data.datasharetest/weather 293 294**表3** module.json5中proxyData节点对应的属性字段 295 296| 属性名称 | 备注说明 | 必填 | 297| ----------------------- | ----------------------------- | ---- | 298| uri | 数据使用的URI,是跨应用数据访问的唯一标识。 | 是 | 299| requiredReadPermission | 标识从该数据代理读取数据时所需要的权限,不配置默认不允许其他APP访问数据。支持权限可参考[权限列表](../security/AccessToken/app-permissions.md)。<br>注意:当前静默访问的权限约束方式与DataShareExtensionAbility的权限约束方式不同,请注意区分,切勿混淆,具体可参考[DataShareExtensionAbility章节](share-data-by-datashareextensionability.md)。 | 否 | 300| requiredWritePermission | 标识从该数据代理修改数据时所需要的权限,不配置默认不允许其他APP访问数据。支持权限可参考[权限列表](../security/AccessToken/app-permissions.md)。<br>注意:当前静默访问的权限约束方式与DataShareExtensionAbility的权限约束方式不同,请注意区分,切勿混淆,具体可参考[DataShareExtensionAbility章节](share-data-by-datashareextensionability.md)。 | 否 | 301 302**module.json5配置样例:** 303 304```json 305// 以下配置仅为示例,应用需根据实际情况配置各个字段 306"proxyData": [ 307 { 308 "uri": "datashareproxy://com.acts.ohos.data.datasharetest/weather", 309 // 实际请按照应用具体场景需要的安全权限配置,如配置应用自定义权限、系统权限或用户授权权限,当前权限仅为示例 310 "requiredReadPermission": "ohos.permission.READ_WEATHER_DATA", 311 "requiredWritePermission": "ohos.permission.KEEP_BACKGROUND_RUNNING" 312 } 313] 314``` 315 316### 数据访问方应用的开发 317 3181. 导入基础依赖包。 319 320 ```ts 321 import { dataShare } from '@kit.ArkData'; 322 import { UIAbility } from '@kit.AbilityKit'; 323 import { window } from '@kit.ArkUI'; 324 import { BusinessError } from '@kit.BasicServicesKit'; 325 ``` 326 3272. 定义与数据提供方通信的URI字符串。 328 329 ```ts 330 let dseUri = ('datashareproxy://com.acts.ohos.data.datasharetest/weather'); 331 ``` 332 3333. 创建工具接口类对象。 334 335 ```ts 336 let dsHelper: dataShare.DataShareHelper | undefined = undefined; 337 let abilityContext: Context; 338 339 export default class EntryAbility extends UIAbility { 340 onWindowStageCreate(windowStage: window.WindowStage) { 341 abilityContext = this.context; 342 dataShare.createDataShareHelper(abilityContext, dseUri, {isProxy : true}, (err, data) => { 343 dsHelper = data; 344 }); 345 } 346 } 347 ``` 348 3494. 获取到接口类对象后,便可利用其提供的接口访问提供方提供的服务,如进行数据的增、删、改、查等。 350 351 ```ts 352 // 构建两条数据,第一条为免配置的数据,仅自己使用 353 let data : Array<dataShare.PublishedItem> = [ 354 {key:"city", subscriberId:"11", data:"xian"}, 355 {key:"datashareproxy://com.acts.ohos.data.datasharetest/weather", subscriberId:"11", data:JSON.stringify("Qing")}]; 356 // 发布数据 357 if (dsHelper != undefined) { 358 let result: Array<dataShare.OperationResult> = await (dsHelper as dataShare.DataShareHelper).publish(data, "com.acts.ohos.data.datasharetestclient"); 359 } 360 ``` 361 3625. 对指定的数据进行订阅。 363 364 ```ts 365 function onPublishCallback(err: BusinessError, node:dataShare.PublishedDataChangeNode) { 366 console.info("onPublishCallback"); 367 } 368 let uris:Array<string> = ["city", "datashareproxy://com.acts.ohos.data.datasharetest/weather"]; 369 if (dsHelper != undefined) { 370 let result: Array<dataShare.OperationResult> = (dsHelper as dataShare.DataShareHelper).on("publishedDataChange", uris, "11", onPublishCallback); 371 } 372 ``` 373 374## 动态数据实现说明 375 376动态数据实现静默访问只针对数据提供方。以动态开启静默访问为例,说明开发步骤。 377 378### 数据提供方应用的开发 379 380数据提供方调用开启动态开启静默访问接口,来开启静默访问功能。此接口是搭配data_share_config.json文件中isSilentProxyEnable字段进行工作的。支持的配置可参考[data_share_config.json配置](./share-data-by-datashareextensionability.md)。 381 382> 注意: 383> 384> - 该步骤为可选,可以不对data_share_config.json文件中isSilentProxyEnable字段进行配置,默认为true,默认为开启静默访问功能。 385> - 校验静默访问是否开启,会优先校验enableSilentProxy/disableSilentProxy接口设置的开关状态,其次会校验data_share_config.json文件中isSilentProxyEnable字段。 386> - 不调用enableSilentProxy/disableSilentProxy接口时,优先会校验data_share_config.json文件中isSilentProxyEnable字段。 387> - 不调用enableSilentProxy/disableSilentProxy接口,也不配置data_share_config.json文件中isSilentProxyEnable字段时,默认静默访问是开启的。 388 3891. 导入基础依赖包。 390 391 ```ts 392 import { dataShare } from '@kit.ArkData'; 393 import { UIAbility } from '@kit.AbilityKit'; 394 import { window } from '@kit.ArkUI'; 395 ``` 396 3972. 定义与数据提供方通信的URI字符串。 398 399 ```ts 400 let dseUri = ('datashare:///com.ohos.settingsdata/entry/DB00/TBL00'); 401 ``` 402 4033. 创建工具接口类对象。 404 405 ```ts 406 let abilityContext: Context; 407 408 export default class EntryAbility extends UIAbility { 409 onWindowStageCreate(windowStage: window.WindowStage) { 410 abilityContext = this.context; 411 dataShare.enableSilentProxy(abilityContext, dseUri); 412 } 413 } 414 ``` 415 416 417 418