HarmonyOS基礎技術賦能之分布式數(shù)據(jù)服務功能
引言
分布式數(shù)據(jù)服務(Distributed Data Service,DDS) 為應用程序提供不同設備間數(shù)據(jù)庫數(shù)據(jù)分布式的能力。通過調用分布式數(shù)據(jù)接口,應用程序將數(shù)據(jù)保存到分布式數(shù)據(jù)庫中。通過結合帳號、應用和數(shù)據(jù)庫三元組,分布式數(shù)據(jù)服務對屬于不同應用的數(shù)據(jù)進行隔離,保證不同應用之間的數(shù)據(jù)不能通過分布式數(shù)據(jù)服務互相訪問。在通過可信認證的設備間,分布式數(shù)據(jù)服務支持應用數(shù)據(jù)相互同步,為用戶提供在多種終端設備上最終一致的數(shù)據(jù)訪問體驗。
功能介紹
此次通過HarmonyOS的分布式數(shù)據(jù)服務能力,一方面可以實現(xiàn)自身應用界面的數(shù)據(jù)實時更新;另一方面也可以實現(xiàn)不同設備之間的數(shù)據(jù)實時更新。前提是在不同設備之間,要實現(xiàn)分布式數(shù)據(jù)服務的同步能力,需要同一個華為賬號登錄、并一個應用包名、同一個網(wǎng)絡之間進行,也可以兩個設備同時開啟藍牙。
開發(fā)指南
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());
- //實現(xiàn)Ability的代碼中顯式聲明需要使用多設備協(xié)同訪問的權限
- requestPermissionsFromUser(new String[]{
- "ohos.permission.DISTRIBUTED_DATASYNC"}, 0);
- }
3. 根據(jù)配置構造分布式數(shù)據(jù)庫管理類實例KvManager以及創(chuàng)建分布式數(shù)據(jù)庫對象SingleKvStore。
- //實現(xiàn)數(shù)據(jù)庫的初始化
- // 初入的參數(shù)context: Context context = getApplicationContext()獲得;storeId為分布式數(shù)據(jù)庫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) //數(shù)據(jù)庫類型:單版本分布式數(shù)據(jù)庫
- .setAutoSync(true);//設置數(shù)據(jù)為自動同步
- singleKvStore = kvManager.getKvStore(options, storeId);
- return singleKvStore;
- }
4. 將數(shù)據(jù)寫入單版本分布式數(shù)據(jù)庫。
- //以key-value形式存儲到分布式數(shù)據(jù)庫
- 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.訂閱分布式數(shù)據(jù)變化??蛻舳诵枰獙崿F(xiàn)KvStoreObserver接口,監(jiān)聽數(shù)據(jù)變化。
- 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) {
- //刷新頁面上的數(shù)據(jù),同樣有一個坑,onChange方法實質上,在一個子線程里執(zhí)行
- MainAbilitySlice.taskDispatcher.asyncDispatch(() -> {
- //在這里執(zhí)行頁面ui組件的顯示刷新
- flushUIData();
- });
- }
- }
6.獲取分布式數(shù)據(jù)庫數(shù)據(jù)
- private void flushUIData() {
- //查詢分布式數(shù)據(jù)的數(shù)據(jù),獲取數(shù)據(jù)可以通過get(String key)/ getEntries(String key)方法獲取數(shù)據(jù)
- List<Entry> 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. 同步數(shù)據(jù)到其他設備。獲取已連接的設備列表,選擇同步方式進行數(shù)據(jù)同步
- List<DeviceInfo> deviceInfoList = kvManager.getConnectedDevicesInfo(DeviceFilterStrategy.NO_FILTER);
- List<String> deviceIdList = new ArrayList<>();
- for (DeviceInfo deviceInfo : deviceInfoList) {
- deviceIdList.add(deviceInfo.getId());
- }
- singleKvStore.sync(deviceIdList, SyncMode.PUSH_ONLY);
項目中采用在后臺service中開啟定時任務,實時保存數(shù)據(jù)到分布式數(shù)據(jù)庫,然后在主界面,監(jiān)聽數(shù)據(jù)變化,實時更新數(shù)據(jù)。
結果演示
1.剛開始安裝完成后效果:

2.每隔3秒,界面數(shù)據(jù)都會發(fā)生變化:


附上源碼:
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 {
- //獲取數(shù)據(jù)庫
- 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) {
- //刷新頁面上的數(shù)據(jù),同樣有一個坑,onChange方法實質上,在一個子線程里執(zhí)行
- getUITaskDispatcher().asyncDispatch(() -> {
- //在這里執(zhí)行頁面ui組件的顯示刷新
- flushUIData();
- });
- }
- }
- private void flushUIData() {
- //查詢分布式數(shù)據(jù)的數(shù)據(jù)
- List<Entry> 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);
- //刪除數(shù)據(jù)庫
- 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 {
- //分布式數(shù)據(jù)庫storeId
- public static final String STORE_ID="kvStoreDB";
- private static KvManager kvManager;
- private static SingleKvStore singleKvStore;
- //具體的實現(xiàn)數(shù)據(jù)庫的初始化
- 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);//設置數(shù)據(jù)為自動同步
- singleKvStore = kvManager.getKvStore(options, storeId);
- return singleKvStore;
- }
- // 如果數(shù)據(jù)庫中的字段有修改,只能先關閉,后刪除,然后重新創(chuàng)建才生效
- 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());
- //實現(xiàn)Ability的代碼中顯式聲明需要使用多設備協(xié)同訪問的權限
- 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布局文件
- <?xml version="1.0" encoding="utf-8"?>
- <DirectionalLayout
- xmlns:ohos="http://schemas.huawei.com/res/ohos"
- ohos:height="match_parent"
- ohos:orientation="vertical"
- ohos:width="match_parent">
- <DirectionalLayout
- ohos:padding="20vp"
- ohos:height="match_content"
- ohos:width="match_parent"
- ohos:orientation="horizontal">
- <Text
- ohos:width="match_content"
- ohos:height="match_content"
- ohos:text_size="20vp"
- ohos:text="溫度:"/>
- <Text
- ohos:id="$+id:tvTemp"
- ohos:width="0"
- ohos:height="match_content"
- ohos:text_size="22vp"
- ohos:text_color="#00ff00"
- ohos:text="待采集..."
- ohos:weight="1"/>
- </DirectionalLayout>
- <DirectionalLayout
- ohos:height="1vp"
- ohos:width="match_parent"
- ohos:background_element="#cccccc"/>
- <DirectionalLayout
- ohos:padding="20vp"
- ohos:height="match_content"
- ohos:width="match_parent"
- ohos:orientation="horizontal">
- <Text
- ohos:width="match_content"
- ohos:height="match_content"
- ohos:text_size="20vp"
- ohos:text="濕度:"/>
- <Text
- ohos:id="$+id:tvHumi"
- ohos:width="0"
- ohos:height="match_content"
- ohos:text_size="22vp"
- ohos:text_color="#00ff00"
- ohos:text="待采集..."
- ohos:weight="1"/>
- </DirectionalLayout>
- <DirectionalLayout
- ohos:height="1vp"
- ohos:width="match_parent"
- ohos:background_element="#cccccc"/>
- <DirectionalLayout
- ohos:padding="20vp"
- ohos:height="match_content"
- ohos:width="match_parent"
- ohos:orientation="horizontal">
- <Text
- ohos:width="match_content"
- ohos:height="match_content"
- ohos:text_size="20vp"
- ohos:text="可燃氣體:"/>
- <Text
- ohos:id="$+id:tvGas"
- ohos:width="0"
- ohos:height="match_content"
- ohos:text_size="22vp"
- ohos:text_color="#00ff00"
- ohos:text="待采集..."
- ohos:weight="1"/>
- </DirectionalLayout>
- <DirectionalLayout
- ohos:height="1vp"
- ohos:width="match_parent"
- ohos:background_element="#cccccc"/>
- </DirectionalLayout>