Demo效果展示
先来看看我们要实现的demo的效果。点击同步数据的按钮后,A设备广播一段字符串到数据库中,然后B设备读取到变化后将接收到的字符串渲染出来。
分布式数据服务概述
HarmonyOS提供了我们在不同设备之间可以获取相同的数据,称之为分布式数据库,只要是信任的设备就都可以获取到保存在分布式数据库里面的数据。所以一般都是在不同设备之间要获取相同数据的场景下使用该数据库。
KV数据模型
“KV数据模型”是“Key-Value数据模型”的简称,“Key-Value”即“键-值”。它是一种NoSQL(非关系型数据存储系统)数据库,其数据以键值对的形式进行组织、索引和存储。
因为KV数据模型用于不涉及过多数据关系和业务关系的数据存储,所以比传统的SQL数据存储拥有更多的读写性能。
分布式数据库同步
底层通信组件完成设备发现和认证,会通知上层应用程序(包括分布式数据服务)设备上线。收到设备上线的消息后分布式数据服务可以在两个设备之间建立加密的数据传输通道,利用该通道在两个设备之间进行数据同步。
分布式数据服务提供了两种同步方式:手动同步和自动同步。
- 手动同步:由应用程序调用sync接口来触发,需要指定同步的设备列表和同步模式。同步模式分为PULL_ONLY(将远端数据拉到本端)、PUSH_ONLY(将本端数据推送到远端)和PUSH_PULL(将本端数据推送到远端同时也将远端数据拉取到本端)。内部接口支持按条件过滤同步,将符合条件的数据同步到远端。
- 自动同步:包括全量同步和按条件订阅同步。全量同步由分布式数据库自动将本端数据推送到远端,同时也将远端数据拉取到本端来完成数据同步,同步时机包括设备上线、应用程序更新数据等,应用不需要主动调用sync接口;内部接口支持按条件订阅同步,将远端符合订阅条件的数据自动同步到本端。
单版本分布式数据库
单版本是指数据在本地保存是以单个KV条目为单位的方式保存,对每个Key最多只保存一个条目项,当数据在本地被用户修改时,不管它是否已经被同步出去,均直接在这个条目上进行修改。同步也以此为基础,按照它在本地被写入或更改的顺序将当前最新一次修改逐条同步至远端设备。
简单来说单版本分布式数据库是通过键值对(Key、Value)的形式保存数据,只能有一个key不能重复,当本地key数据被修改的时候会被直接修改,然后将最新的数据再同步出去。
设备协同分布式数据库
设备协同分布式数据库建立在单版本分布式数据库之上,对应用程序存入的KV数据中的Key前面拼接了本设备的DeviceID标识符,这样能保证每个设备产生的数据严格隔离,底层按照设备的维度管理这些数据,设备协同分布式数据库支持以设备的维度查询分布式数据,但是不支持修改远端设备同步过来的数据。
简单来说就是在单版本分布式数据库的key前面拼接上本设备的设备id,保证每个设备的数据库能够区分,知道是哪个设备添加进去的数据;但是不能修改远端设备同步过来的数据,就是在A设备添加的键值对数据推送到B设备,B设备只能读取该数据,不能对该数据进行修改。
下面我们简单写一个分布式数据库的demo,进入代码实战环节。
代码封装
一个项目中,用到分布式数据库的地方可能不止一处。为了提高代码的复用性以及方便后续维护,就需要对这部分代码进行封装。
所以我们这里先新建一个模块文件,然后再该文件内新建一个js模块(这里我创建的模块命名为kv.js),在这里面将分布式数据服务的代码进行封装。后续要使用到相关的功能我们只需要调用这个文件即可,可以有效避免重复性的工作同时提高代码的可读性。
导入模块
创建一个单独的js文件,进行数据库模块封装,方便后续调用。
import distributedData from '@ohos.data.distributedData';
创建分布式数据库
创建分布式数据库分为三步:
- 创建KvManagerConfig对象。
- 其中bundleName要和自己创建的工程保持一致。
- 创建分布式数据库管理器。
- 这里option中的kvStoreType参数要注意一下,0是设备协同分布式数据库,1是单版本数据库,区别上面已经提到,如果没有特殊业务,一般选择单版本数据库。
- 其中option中的securityLevel等级越高,限制越多。
- 获取分布式数据库。
下面是具体实现代码:
createKvStore(callback) {
if (typeof (this.kvStore) === 'undefined') {
//创建KvManagerConfig对象
var config = {
bundleName: 'com.example.myapplication',
userInfo: {
userId: '0',
userType: 0
}
};
let self = this;
distributedData.createKVManager(config).then((manager) => {
//创建分布式数据库管理器
self.kvManager = manager;
var options = {
createIfMissing: true,
encrypt: false,
backup: false,
autoSync: true,
kvStoreType: 1,
//等级越高,限制越多
securityLevel: 1,
};
self.kvManager.getKVStore(STORE_ID, options).then((store) => {
self.kvStore = store;
callback();
});
});
} else {
callback();
}
}
增删改查
增删改查接口就比较简单,先来简单看一下api接口功能及参数:
- 插入和更新数据:put(key: string, value: Uint8Array | string | number | boolean, callback: AsyncCallback
): void put(key: string, value: Uint8Array | string | number | boolean): Promise 。 - 删除数据:delete(key: string, callback: AsyncCallback
): void delete(key: string): Promise 。 - 查询数据:get(key: string, callback: AsyncCallback
): void get(key: string): Promise 。
然后接下来我们来编写增删改查的代码
插入和更新数据
sendMessage(key,value) {
let self = this;
this.createKvStore(() => {
self.put(key,value);
});
}
put(key,value) {
console.info('data is changing...' );
this.kvStore.put(key,value).then((data) => {
console.info('dataChange:' + JSON.stringify(data));
this.kvStore.get(key).then((data) => {
console.info('dataChange:' + JSON.stringify(data));
});
}).catch((err) => {
});
}
删除数据
delete(key) {
this.kvStore.delete(key).then((data) => {
}).catch((err) => {
});
}
查询数据
get(key) {
this.kvStore.get(key).then((data) => {
}).catch((err) => {
});
}
订阅数据变化
- 订阅数据变化:on(event: ‘dataChange’, type: SubscribeType, observer: Callback
): void on(event: ‘syncComplete’, syncCallback: Callback >): void。
setOnMessageReceivedListener(callback) {
let self = this;
this.createKvStore(() => {
self.kvStore.on('dataChange', 1, (data) => {
console.info('dataChange:' + JSON.stringify(data));
for (var i = 0; i < data.updateEntries.length; i++) {
var str = data.updateEntries[0].value.value;
callback(data.updateEntries[0].key,str, 1);
return;
}
});
});
}
到此我们就已经完成了代码的封装。
代码调用
封装好了之后,我们接下来只需要进行简单的调用即可,首先需要创建一个page,这里我们就拿index这个页面来进行一个简单的demo编写。
步骤只有三步:
- 获取相关权限
- 封装模块调用
- 界面渲染
获取permisssion权限
在config.json中添加permisssion权限
"reqPermissions": [
{
"name": "ohos.permission.DISTRIBUTED_DATASYNC"
}
]
添加位置如下图:
在初始页面申请用户权限
编写一个申请权限函数,然后再初始化页面时调用。
import featureAbility from '@ohos.ability.featureAbility';
export default {
onInit() {
this.grantPermission();
},
grantPermission() {
console.info('Calc[IndexPage] grantPermission')
let context = featureAbility.getContext()
context.requestPermissionsFromUser(['ohos.permission.DISTRIBUTED_DATASYNC'], 666, function (res) {
console.log("chw---get permission result:"+res)
})
},
}
封装模块调用
导入已封装的分布数数据库模块
import KvStoreModel from '../../model/kv.js';
创建实例
data: {
kvStoreModel: new KvStoreModel(),
},
订阅分布式数据库消息通知
onInit() {
console.log("chw---init start")
this.grantPermission();
// type表示操作0:插入数据、1:修改数据:2:删除数据
this.kvStoreModel.setOnMessageReceivedListener((k, v, type1) => {
console.log("chw---is listening")
//传过来是的字符串,要解序列化为 JSON对象
this.testReceiveStr =v;
this.isReceiveInfo= true;
this.changeType=type1;
console.log("chw---receive data:"+v);
});
console.log("chw---init end")
},
广播同步数据
send(){
var that=this;
//这里注意如果发送数据类型是对象或者数组需要将对象或者数组序列化成 JSON字符串
this.kvStoreModel.sendMessage(that.key,"消息123");
console.log("chw---is sending")
},
js完整代码
import featureAbility from '@ohos.ability.featureAbility';
import KvStoreModel from '../../model/kv.js';
export default {
data: {
kvStoreModel: new KvStoreModel(),
isReceiveInfo:false,
changeType:1,
key:"openharmony",
testReceiveStr:"nothing"
},
onInit() {
console.log("chw---init start")
this.grantPermission();
// type表示操作0:插入数据、1:修改数据:2:删除数据
this.kvStoreModel.setOnMessageReceivedListener((k, v, type1) => {
console.log("chw---is listening")
//传过来是的字符串,要解序列化为 JSON对象
this.testReceiveStr =v;
this.isReceiveInfo= true;
this.changeType=type1;
console.log("chw---receive data:"+v);
});
console.log("chw---init end")
},
grantPermission() {
let context = featureAbility.getContext()
context.requestPermissionsFromUser(['ohos.permission.DISTRIBUTED_DATASYNC'], 666, function (res) {
// console.log("chw---get permission result:"+res)
})
},
send(){
var that=this;
//这里注意如果发送数据类型是对象或者数组需要将对象或者数组序列化成 JSON字符串
this.kvStoreModel.sendMessage(that.key,"消息123");
console.log("chw---is sending")
},
}
界面渲染
为了方便看到数据同步的情况,当然,也可以打开控制台查看打印的信息。
这里我们选择直接渲染出数据同步情况,在hml文件中添加以下代码:
<div class="container">
<text class="title" if="{{isReceiveInfo}}">
接收信息:{{ testReceiveStr }}
text>
<button type="capsule" onclick="send" style="width: 300px;height: 100px;">
同步数据
button>
div>
这样我们就完整做完一个分布式数据库的小demo了。