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![silent_dataShare](figures/silent_dataShare.jpg)
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”参数。当使用多个"?"符开始参数时,"?"后的参数需是"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## 约束与限制
61
62- 目前持久化数据中仅关系型数据库支持静默数据访问方式。
63- 整个系统最多同时并发16路查询,有多出来的查询请求需要排队处理。
64- 持久化数据不支持代理创建数据库,如果需要创建数据库,需要拉起数据提供方。
65- 数据提供方如果是normal级别签名的应用,配置的数据读写权限必须为system_basic及以上权限。
66
67
68## 接口说明
69
70以下是静默数据访问的相关接口,大部分为异步接口。异步接口均有callback和Promise两种返回形式,下表均以callback形式为例,更多接口及使用方式请见[数据共享](../reference/apis-arkdata/js-apis-data-dataShare-sys.md)。
71
72### 通用接口
73
74| 接口名称                                     | 描述                   |
75| ---------------------------------------- | -------------------- |
76| createDataShareHelper(context: Context, uri: string, options: DataShareHelperOptions, callback: AsyncCallback<DataShareHelper>): void | 创建DataShareHelper实例。 |
77
78### 持久化数据
79
80| 接口名称                                     | 描述                   |
81| ---------------------------------------- | -------------------- |
82| insert(uri: string, value: ValuesBucket, callback: AsyncCallback<number>): void | 向目标表中插入一行数据。         |
83| delete(uri: string, predicates: dataSharePredicates.DataSharePredicates, callback: AsyncCallback<number>): void | 从数据库中删除一条或多条数据记录。    |
84| query(uri: string, predicates: dataSharePredicates.DataSharePredicates, columns: Array<string>, callback: AsyncCallback<DataShareResultSet>): void | 查询数据库中的数据。           |
85| update(uri: string, predicates: dataSharePredicates.DataSharePredicates, value: ValuesBucket, callback: AsyncCallback<number>): void | 更新数据库中的数据记录。         |
86| addTemplate(uri: string, subscriberId: string, template: Template): void | 添加一个指定订阅者的数据模板。      |
87| on(type: 'rdbDataChange', uris: Array<string>, templateId: TemplateId, callback: AsyncCallback<RdbDataChangeNode>): Array<OperationResult | 订阅指定URI和模板对应的数据变更事件。 |
88
89### 过程数据
90
91| 接口名称                                     | 描述                 |
92| ---------------------------------------- | ------------------ |
93| publish(data: Array<PublishedItem>, bundleName: string, version: number, callback: AsyncCallback<Array<OperationResult>>): void | 发布数据,将数据托管至数据管理服务。 |
94| on(type: 'publishedDataChange', uris: Array<string>, subscriberId: string, callback: AsyncCallback<PublishedDataChangeNode>): Array<OperationResult> | 订阅已发布数据的数据变更通知。    |
95
96### 动态数据
97
98| 接口名称                                     | 描述                 |
99| ---------------------------------------- | ------------------ |
100| enableSilentProxy(context: Context, uri?: string): Promise&lt;void&gt; | 数据提供方动态开启静默访问。<br />当访问方通过静默访问调用DataShare相关接口的时候,校验静默访问的开关状态。<br />如果静默访问的是开启的,DataShare相关接口会执行原逻辑。 |
101| disableSilentProxy(context: Context, uri?: string): Promise&lt;void&gt; | 数据提供方来动态关闭静默访问。<br />当访问方通过静默访问调用DataShare相关接口的时候,校验静默访问的开关状态。<br />如果静默访问的是关闭的,DataShare相关接口接口将会直接返回。 |
102
103
104
105## 持久化数据实现说明
106
107首先,以共享一个关系型数据库为例,说明开发步骤。
108
109### 数据提供方应用的开发
110
1111. 数据提供方需要在module.json5中的proxyData节点定义要共享的表的标识,读写权限和基本信息, 配置方法可考参考[配置文件](../quick-start/module-configuration-file.md)。
112
113   **表1** module.json5中proxyData节点对应的属性字段
114
115   | 属性名称                    | 备注说明                                     | 必填   |
116   | ----------------------- | ---------------------------------------- | ---- |
117   | uri                     | 数据使用的URI,是跨应用数据访问的唯一标识。                  | 是    |
118   | requiredReadPermission  | 标识从该数据代理读取数据时所需要的权限,不配置默认不允许其他APP访问数据。支持权限可参考[权限列表](../security/AccessToken/app-permissions.md)。            | 否    |
119   | requiredWritePermission | 标识从该数据代理修改数据时所需要的权限,不配置默认不允许其他APP修改数据。支持权限可参考[权限列表](../security/AccessToken/app-permissions.md)。          | 否    |
120   | metadata                | 数据源的信息,包含name和resource字段。<br /> name类型固定为"dataProperties",是配置的唯一标识。 <br /> resource类型固定为"$profile:{fileName}",表示配置文件的名称为{fileName}.json。 | 是    |
121
122   **module.json5配置样例:**
123
124   ```json
125   "proxyData":[
126     {
127       "uri": "datashareproxy://com.acts.ohos.data.datasharetest/test",
128       "requiredReadPermission": "ohos.permission.GET_BUNDLE_INFO",
129       "requiredWritePermission": "ohos.permission.KEEP_BACKGROUND_RUNNING",
130       "metadata": {
131         "name": "dataProperties",
132         "resource": "$profile:my_config"
133       }
134     }
135   ]
136   ```
137   **表2** my_config.json对应属性字段
138
139   | 属性名称  | 备注说明                                     | 必填   |
140   | ----- | ---------------------------------------- | ---- |
141   | path  | 指定数据源路径,目前支持关系型数据库,配置为库名/表名              | 是    |
142   | type  | 标识数据库类型,目前支持配置为rdb,表示关系型数据库。             | 是    |
143   | scope | 数据库所在范围。<br>1.module表示数据库位于本模块下;<br>2.application表示数据库位于本应用下。 | 否    |
144
145   **my_config.json配置样例**
146
147   ```json
148   {
149     "path": "DB00/TBL00",
150     "type": "rdb",
151     "scope": "application"
152   }
153   ```
154
155### 数据访问方应用的开发
156
157
1581. 导入基础依赖包。
159
160   ```ts
161   import { dataShare, dataSharePredicates, ValuesBucket } from '@kit.ArkData';
162   import { UIAbility } from '@kit.AbilityKit';
163   import { window } from '@kit.ArkUI';
164   import { BusinessError } from '@kit.BasicServicesKit'
165   ```
166
1672. 定义与数据提供方通信的URI字符串。
168
169   ```ts
170   let dseUri = ('datashareproxy://com.acts.ohos.data.datasharetest/test');
171   ```
172
1733. 创建工具接口类对象。
174
175   ```ts
176   let dsHelper: dataShare.DataShareHelper | undefined = undefined;
177   let abilityContext: Context;
178
179   export default class EntryAbility extends UIAbility {
180     onWindowStageCreate(windowStage: window.WindowStage) {
181       abilityContext = this.context;
182       dataShare.createDataShareHelper(abilityContext, dseUri, {
183         isProxy: true
184       }, (err, data) => {
185         dsHelper = data;
186       });
187     }
188   }
189   ```
190
1914. 获取到接口类对象后,便可利用其提供的接口访问提供方提供的服务,如进行数据的增、删、改、查等。
192
193   ```ts
194   // 构建一条数据
195   let key1 = 'name';
196   let key2 = 'age';
197   let key3 = 'isStudent';
198   let key4 = 'Binary';
199   let valueName1 = 'ZhangSan';
200   let valueName2 = 'LiSi';
201   let valueAge1 = 21;
202   let valueAge2 = 18;
203   let valueIsStudent1 = false;
204   let valueIsStudent2 = true;
205   let valueBinary = new Uint8Array([1, 2, 3]);
206   let valuesBucket: ValuesBucket = { key1: valueName1, key2: valueAge1, key3: valueIsStudent1, key4: valueBinary };
207   let updateBucket: ValuesBucket = { key1: valueName2, key2: valueAge2, key3: valueIsStudent2, key4: valueBinary };
208   let predicates = new dataSharePredicates.DataSharePredicates();
209   let valArray = ['*'];
210   if (dsHelper != undefined) {
211     // 插入一条数据
212     (dsHelper as dataShare.DataShareHelper).insert(dseUri, valuesBucket, (err, data) => {
213       console.info(`dsHelper insert result:${data}`);
214     });
215     // 更新数据
216     (dsHelper as dataShare.DataShareHelper).update(dseUri, predicates, updateBucket, (err, data) => {
217       console.info(`dsHelper update result:${data}`);
218     });
219     // 查询数据
220     (dsHelper as dataShare.DataShareHelper).query(dseUri, predicates, valArray, (err, data) => {
221       console.info(`dsHelper query result:${data}`);
222     });
223     // 删除指定的数据
224     (dsHelper as dataShare.DataShareHelper).delete(dseUri, predicates, (err, data) => {
225       console.info(`dsHelper delete result:${data}`);
226     });
227   }
228   ```
229
2305. 对指定的数据进行订阅。
231
232   ```ts
233   function onCallback(err: BusinessError, node: dataShare.RdbDataChangeNode) {
234     console.info("uri " + JSON.stringify(node.uri));
235     console.info("templateId " + JSON.stringify(node.templateId));
236     console.info("data length " + node.data.length);
237     for (let i = 0; i < node.data.length; i++) {
238       console.info("data " + node.data[i]);
239     }
240   }
241
242   let key21: string = "p1";
243   let value21: string = "select * from TBL00";
244   let key22: string = "p2";
245   let value22: string = "select name from TBL00";
246   let template: dataShare.Template = {
247     predicates: {
248       key21: value21,
249       key22: value22,
250     },
251     scheduler: ""
252   }
253   if(dsHelper != undefined)
254   {
255     (dsHelper as dataShare.DataShareHelper).addTemplate(dseUri, "111", template);
256   }
257   let templateId: dataShare.TemplateId = {
258     subscriberId: "111",
259     bundleNameOfOwner: "com.acts.ohos.data.datasharetestclient"
260   }
261   if(dsHelper != undefined) {
262     // 使用数据管理服务修改数据时触发onCallback回调,回调内容是template中的规则查到的数据
263     let result: Array<dataShare.OperationResult> = (dsHelper as dataShare.DataShareHelper).on("rdbDataChange", [dseUri], templateId, onCallback);
264   }
265   ```
266
267## 过程数据实现说明
268
269以托管一份过程数据为例,说明开发步骤。
270
271### 数据提供方应用的开发(可选)
272
273数据提供方需要在module.json5中的proxyData节点定义过程数据的标识,读写权限和基本信息, 配置方法可考参考[配置文件](../quick-start/module-configuration-file.md)。
274
275> 注意:
276>
277> - 该步骤为可选,可以不对module.json5中的proxyData进行配置。
278> - 不配置proxyData时,托管数据不允许其他应用访问。
279> - 不配置proxyData时,数据标识可以为简写,发布、订阅、查询数据可以使用简写的数据标识,如weather,可以不用全写为datashareproxy://com.acts.ohos.data.datasharetest/weather
280
281**表3** module.json5中proxyData节点对应的属性字段
282
283| 属性名称                    | 备注说明                          | 必填   |
284| ----------------------- | ----------------------------- | ---- |
285| uri                     | 数据使用的URI,是跨应用数据访问的唯一标识。       | 是    |
286| requiredReadPermission  | 标识从该数据代理读取数据时所需要的权限,不配置默认不允许其他APP访问数据。支持权限可参考[权限列表](../security/AccessToken/app-permissions.md)。 | 否    |
287| requiredWritePermission | 标识从该数据代理修改数据时所需要的权限,不配置默认不允许其他APP访问数据。支持权限可参考[权限列表](../security/AccessToken/app-permissions.md)。 | 否    |
288
289**module.json5配置样例:**
290
291```json
292"proxyData": [
293  {
294    "uri": "datashareproxy://com.acts.ohos.data.datasharetest/weather",
295    "requiredReadPermission": "ohos.permission.GET_BUNDLE_INFO",
296    "requiredWritePermission": "ohos.permission.KEEP_BACKGROUND_RUNNING"
297  }
298]
299```
300
301### 数据访问方应用的开发
302
3031. 导入基础依赖包。
304
305   ```ts
306   import { dataShare } from '@kit.ArkData';
307   import { UIAbility } from '@kit.AbilityKit';
308   import { window } from '@kit.ArkUI';
309   import { BusinessError } from '@kit.BasicServicesKit';
310   ```
311
3122. 定义与数据提供方通信的URI字符串。
313
314   ```ts
315   let dseUri = ('datashareproxy://com.acts.ohos.data.datasharetest/weather');
316   ```
317
3183. 创建工具接口类对象。
319
320   ```ts
321   let dsHelper: dataShare.DataShareHelper | undefined = undefined;
322   let abilityContext: Context;
323
324   export default class EntryAbility extends UIAbility {
325     onWindowStageCreate(windowStage: window.WindowStage) {
326       abilityContext = this.context;
327       dataShare.createDataShareHelper(abilityContext, dseUri, {isProxy : true}, (err, data) => {
328         dsHelper = data;
329       });
330     }
331   }
332   ```
333
3344. 获取到接口类对象后,便可利用其提供的接口访问提供方提供的服务,如进行数据的增、删、改、查等。
335
336   ```ts
337   // 构建两条数据,第一条为免配置的数据,仅自己使用
338   let data : Array<dataShare.PublishedItem> = [
339     {key:"city", subscriberId:"11", data:"xian"},
340     {key:"datashareproxy://com.acts.ohos.data.datasharetest/weather", subscriberId:"11", data:JSON.stringify("Qing")}];
341   // 发布数据
342   if (dsHelper != undefined) {
343     let result: Array<dataShare.OperationResult> = await (dsHelper as dataShare.DataShareHelper).publish(data, "com.acts.ohos.data.datasharetestclient");
344   }
345   ```
346
3475. 对指定的数据进行订阅。
348
349   ```ts
350   function onPublishCallback(err: BusinessError, node:dataShare.PublishedDataChangeNode) {
351     console.info("onPublishCallback");
352   }
353   let uris:Array<string> = ["city", "datashareproxy://com.acts.ohos.data.datasharetest/weather"];
354   if (dsHelper != undefined) {
355     let result: Array<dataShare.OperationResult> = (dsHelper as dataShare.DataShareHelper).on("publishedDataChange", uris, "11", onPublishCallback);
356   }
357   ```
358
359## 动态数据实现说明
360
361动态数据实现静默访问只针对数据提供方。以动态开启静默访问为例,说明开发步骤。
362
363### 数据提供方应用的开发
364
365数据提供方调用开启动态开启静默访问接口,来开启静默访问功能。此接口是搭配data_share_config.json文件中isSilentProxyEnable字段进行工作的。支持的配置可参考[data_share_config.json配置](./share-data-by-datashareextensionability.md)
366
367> 注意:
368>
369> - 该步骤为可选,可以不对data_share_config.json文件中isSilentProxyEnable字段进行配置,默认为true,默认为开启静默访问功能。
370> - 校验静默访问是否开启,会优先校验enableSilentProxy/disableSilentProxy接口设置的开关状态,其次会校验data_share_config.json文件中isSilentProxyEnable字段。
371> - 不调用enableSilentProxy/disableSilentProxy接口时,优先会校验data_share_config.json文件中isSilentProxyEnable字段。
372> - 不调用enableSilentProxy/disableSilentProxy接口,也不配置data_share_config.json文件中isSilentProxyEnable字段时,默认静默访问是开启的。
373
3741. 导入基础依赖包。
375
376   ```ts
377   import { dataShare } from '@kit.ArkData';
378   import { UIAbility } from '@kit.AbilityKit';
379   import { window } from '@kit.ArkUI';
380   ```
381
3822. 定义与数据提供方通信的URI字符串。
383
384   ```ts
385   let dseUri = ('datashare:///com.acts.datasharetest/entry/DB00/TBL00');
386   ```
387
3883. 创建工具接口类对象。
389
390   ```ts
391   let abilityContext: Context;
392
393   export default class EntryAbility extends UIAbility {
394     onWindowStageCreate(windowStage: window.WindowStage) {
395       abilityContext = this.context;
396       dataShare.enableSilentProxy(abilityContext, dseUri);
397     }
398   }
399   ```
400
401
402
403