引言
分布式数据服务(Distributed Data Service,DDS) 为应用程序提供不同设备间数据库数据分布式的能力。通过调用分布式数据接口,应用程序将数据保存到分布式数据库中。通过结合帐号、应用和数据库三元组,分布式数据服务对属于不同应用的数据进行隔离,保证不同应用之间的数据不能通过分布式数据服务互相访问。在通过可信认证的设备间,分布式数据服务支持应用数据相互同步,为用户提供在多种终端设备上最终一致的数据访问体验。
功能介绍
此次通过HarmonyOS的分布式数据服务能力,一方面可以实现自身应用界面的数据实时更新;另一方面也可以实现不同设备之间的数据实时更新。前提是在不同设备之间,要实现分布式数据服务的同步能力,需要同一个华为账号登录、并一个应用包名、同一个网络之间进行,也可以两个设备同时开启蓝牙。
开发指南
\1. 在config.json中添加permisssion权限。
// 添加在abilities同一目录层级
"reqPermissions": [
{
"name": "ohos.permission.DISTRIBUTED_DATASYNC"
}
]
2. 在MainAbility中添加权限
@Override
public void onStart(Intent intent) {
super.onStart(intent);
super.setMainRoute(MainAbilitySlice.class.getName());
//实现Ability的代码中显式声明需要使用多设备协同访问的权限
requestPermissionsFromUser(new String[]{
"ohos.permission.DISTRIBUTED_DATASYNC"}, 0);
}
3. 根据配置构造分布式数据库管理类实例KvManager以及创建分布式数据库对象SingleKvStore。
//实现数据库的初始化
// 初入的参数context: Context context = getApplicationContext()获得;storeId为分布式数据库id,String类型,可自行定义,例如“testApp”。
public static SingleKvStore initOrGetDB(Context context, String storeId) {
KvManagerConfig kvManagerConfig = new KvManagerConfig(context);
kvManager = KvManagerFactory.getInstance().createKvManager(kvManagerConfig);
Options options = new Options();
options.setCreateIfMissing(true)
.setEncrypt(false)
.setKvStoreType(KvStoreType.SINGLE_VERSION) //数据库类型:单版本分布式数据库
.setAutoSync(true);//设置数据为自动同步
singleKvStore = kvManager.getKvStore(options, storeId);
return singleKvStore;
}
4. 将数据写入单版本分布式数据库。
//以key-value形式存储到分布式数据库
try {
long id = System.currentTimeMillis();
singleKvStore.putString("key",
"{\"id\":" + id +
",\"temp\":" + temperature +
",\"humidity\":" + humidity +
",\"NH4\":" + 0.0 +
",\"H2S\":" + 0.0 +
",\"other\":" + gas + "}");
} catch (KvStoreException e) {
e.printStackTrace();
}
5.订阅分布式数据变化。客户端需要实现KvStoreObserver接口,监听数据变化。
try {
//订阅类型SubscribeType.SUBSCRIBE_TYPE_ALL意思可以同步到本机和其他外围设备
innerKvStoreObserver = new InnerKvStoreObserver();
singleKvStore.subscribe(SubscribeType.SUBSCRIBE_TYPE_ALL, innerKvStoreObserver);
} catch (KvStoreException e) {
e.printStackTrace();
}
public class InnerKvStoreObserver implements KvStoreObserver {
@Override
public void onChange(ChangeNotification changeNotification) {
//刷新页面上的数据,同样有一个坑,onChange方法实质上,在一个子线程里执行
MainAbilitySlice.taskDispatcher.asyncDispatch(() -> {
//在这里执行页面ui组件的显示刷新
flushUIData();
});
}
}
6.获取分布式数据库数据
private void flushUIData() {
//查询分布式数据的数据,获取数据可以通过get(String key)/ getEntries(String key)方法获取数据
List entries = singleKvStore.getEntries(“key”);
if (entries.size() > 0) {
ZSONObject zsonObject = ZSONObject.stringToZSON(entries.get(0).getValue().getString());
int temp = zsonObject.getIntValue("temp");
int humidity = zsonObject.getIntValue("humidity");
int other = zsonObject.getIntValue("other");
tvTemp.setText(temp+"℃");
tvHumi.setText(humidity+"% RH");
tvGas.setText(other+"% LEL");
}
7. 解除订阅。一般在页面销毁时调用,也就是MainAbilitySlice的onStop()中调用
if (singleKvStore != null) {
singleKvStore.unSubscribe(innerKvStoreObserver);
}
8. 同步数据到其他设备。获取已连接的设备列表,选择同步方式进行数据同步
List deviceInfoList = kvManager.getConnectedDevicesInfo(DeviceFilterStrategy.NO_FILTER);
List deviceIdList = new ArrayList<>();
for (DeviceInfo deviceInfo : deviceInfoList) {
deviceIdList.add(deviceInfo.getId());
}
singleKvStore.sync(deviceIdList, SyncMode.PUSH_ONLY);
项目中采用在后台service中开启定时任务,实时保存数据到分布式数据库,然后在主界面,监听数据变化,实时更新数据。
结果演示
1.刚开始安装完成后效果:
2.每隔3秒,界面数据都会发生变化:
附上源码:
1.MainAbilitySlice
public class MainAbilitySlice extends AbilitySlice {
private SingleKvStore singleKvStore;
private Text tvTemp;
private Text tvHumi;
private Text tvGas;
private Intent serviceIntent;
private InnerKvStoreObserver innerKvStoreObserver;
@Override
public void onStart(Intent intent) {
super.onStart(intent);
super.setUIContent(ResourceTable.Layout_ability_main);
tvTemp=(Text)findComponentById(ResourceTable.Id_tvTemp);
tvHumi=(Text)findComponentById(ResourceTable.Id_tvHumi);
tvGas=(Text)findComponentById(ResourceTable.Id_tvGas);
initService();
try {
//获取数据库
singleKvStore = DBUtils.initOrGetDB(this, DBUtils.STORE_ID);
innerKvStoreObserver = new InnerKvStoreObserver();
singleKvStore.subscribe(SubscribeType.SUBSCRIBE_TYPE_ALL, innerKvStoreObserver);
} catch (KvStoreException e) {
e.printStackTrace();
}
}
public class InnerKvStoreObserver implements KvStoreObserver {
@Override
public void onChange(ChangeNotification changeNotification) {
//刷新页面上的数据,同样有一个坑,onChange方法实质上,在一个子线程里执行
getUITaskDispatcher().asyncDispatch(() -> {
//在这里执行页面ui组件的显示刷新
flushUIData();
});
}
}
private void flushUIData() {
//查询分布式数据的数据
List entries = singleKvStore.getEntries("key");
if (entries.size() > 0) {
ZSONObject zsonObject = ZSONObject.stringToZSON(entries.get(0).getValue().getString());
int temp = zsonObject.getIntValue("temp");
int humidity = zsonObject.getIntValue("humidity");
int other = zsonObject.getIntValue("other");
tvTemp.setText(temp+"℃");
tvHumi.setText(humidity+"% RH");
tvGas.setText(other+"% LEL");
}
}
private void initService() {
//启动ServiceAbility
serviceIntent = new Intent();
Operation operation = new Intent.OperationBuilder()
.withDeviceId("")
.withBundleName("com.isoftstone.kvstoreapp")
.withAbilityName("com.isoftstone.kvstoreapp.ServiceAbility")
.build();
serviceIntent.setOperation(operation);
startAbility(serviceIntent);
}
@Override
public void onActive() {
super.onActive();
}
@Override
public void onForeground(Intent intent) {
super.onForeground(intent);
}
@Override
protected void onStop() {
super.onStop();
//销毁service
stopAbility(serviceIntent);
//删除数据库
DBUtils.clearDB();
//解除订阅
if (singleKvStore != null) {
singleKvStore.unSubscribe(innerKvStoreObserver);
}
}
}
2.ServiceAbility
public class ServiceAbility extends Ability {
private static final HiLogLabel LABEL_LOG = new HiLogLabel(3, 0xD001100, "Demo");
private SingleKvStore singleKvStore;
private Timer timer;
private MyTimerTask myTimerTask;
private int temperature;
private int humidity;
private int gas;
@Override
public void onStart(Intent intent) {
super.onStart(intent);
singleKvStore = DBUtils.initOrGetDB(this, DBUtils.STORE_ID);
timer=new Timer();
myTimerTask=new MyTimerTask();
timer.schedule(myTimerTask,0,3000);
}
@Override
public void onBackground() {
super.onBackground();
HiLog.info(LABEL_LOG, "ServiceAbility::onBackground");
}
@Override
public void onStop() {
super.onStop();
if(myTimerTask!=null){
myTimerTask.cancel();
}
if(timer!=null){
timer.cancel();
}
}
@Override
public void onCommand(Intent intent, boolean restart, int startId) {
}
@Override
public IRemoteObject onConnect(Intent intent) {
return null;
}
@Override
public void onDisconnect(Intent intent) {
}
private class MyTimerTask extends TimerTask{
@Override
public void run() {
temperature++;
humidity++;
gas++;
try {
long id = System.currentTimeMillis();
singleKvStore.putString("key",
"{\"id\":" + id +
",\"temp\":" + temperature +
",\"humidity\":" + humidity +
",\"NH4\":" + 0.0 +
",\"H2S\":" + 0.0 +
",\"other\":" + gas + "}");
} catch (KvStoreException e) {
e.printStackTrace();
}
}
}
}
3.DBUtils
public class DBUtils {
//分布式数据库storeId
public static final String STORE_ID="kvStoreDB";
private static KvManager kvManager;
private static SingleKvStore singleKvStore;
//具体的实现数据库的初始化
public static SingleKvStore initOrGetDB(Context context, String storeId) {
KvManagerConfig kvManagerConfig = new KvManagerConfig(context);
kvManager = KvManagerFactory.getInstance().createKvManager(kvManagerConfig);
Options options = new Options();
options.setCreateIfMissing(true)
.setEncrypt(false)
.setKvStoreType(KvStoreType.SINGLE_VERSION)
.setAutoSync(true);//设置数据为自动同步
singleKvStore = kvManager.getKvStore(options, storeId);
return singleKvStore;
}
// 如果数据库中的字段有修改,只能先关闭,后删除,然后重新创建才生效
public static void clearDB() {
kvManager.closeKvStore(singleKvStore);
kvManager.deleteKvStore(STORE_ID);
}
}
4. MainAbility
public class MainAbility extends Ability {
@Override
public void onStart(Intent intent) {
super.onStart(intent);
super.setMainRoute(MainAbilitySlice.class.getName());
//实现Ability的代码中显式声明需要使用多设备协同访问的权限
requestPermissionsFromUser(new String[]{
"ohos.permission.DISTRIBUTED_DATASYNC"}, 0);
}
}
5. MyApplication
public class MyApplication extends AbilityPackage {
@Override
public void onInitialize() {
super.onInitialize();
}
}
6. config.json 文件
{
"app": {
"bundleName": "com.isoftstone.healthdata",
"vendor": "isoftstone",
"version": {
"code": 1000000,
"name": "1.0"
},
"apiVersion": {
"compatible": 4,
"target": 5,
"releaseType": "Release"
}
},
"deviceConfig": {},
"module": {
"package": "com.isoftstone.kvstoreapp",
"name": ".MyApplication",
"deviceType": [
"phone"
],
"distro": {
"deliveryWithInstall": true,
"moduleName": "entry",
"moduleType": "entry"
},
"reqPermissions": [
{
"name": "ohos.permission.DISTRIBUTED_DATASYNC"
}
],
"abilities": [
{
"skills": [
{
"entities": [
"entity.system.home"
],
"actions": [
"action.system.home"
]
}
],
"orientation": "unspecified",
"name": "com.isoftstone.kvstoreapp.MainAbility",
"icon": "$media:icon",
"description": "$string:mainability_description",
"label": "$string:app_name",
"type": "page",
"launchType": "standard"
},
{
"name": "com.isoftstone.kvstoreapp.ServiceAbility",
"icon": "$media:icon",
"description": "$string:serviceability_description",
"type": "service"
}
]
}
}
7.xml布局文件