深圳幻海软件技术有限公司 欢迎您!

HarmonyOS基础技术赋能之分布式数据服务功能

2023-02-27

想了解更多内容,请访问:51CTO和华为官方合作共建的鸿蒙技术社区https://harmonyos.51cto.com引言分布式数据服务(DistributedDataService,DDS)为应用程序提供不同设备间数据库数据分布式的能力。通过调用分布式数据接口,应用程序将数据保存到分布式数据库中

想了解更多内容,请访问:

51CTO和华为官方合作共建的鸿蒙技术社区

https://harmonyos.51cto.com

引言

分布式数据服务(Distributed Data Service,DDS) 为应用程序提供不同设备间数据库数据分布式的能力。通过调用分布式数据接口,应用程序将数据保存到分布式数据库中。通过结合帐号、应用和数据库三元组,分布式数据服务对属于不同应用的数据进行隔离,保证不同应用之间的数据不能通过分布式数据服务互相访问。在通过可信认证的设备间,分布式数据服务支持应用数据相互同步,为用户提供在多种终端设备上最终一致的数据访问体验。

功能介绍

此次通过HarmonyOS的分布式数据服务能力,一方面可以实现自身应用界面的数据实时更新;另一方面也可以实现不同设备之间的数据实时更新。前提是在不同设备之间,要实现分布式数据服务的同步能力,需要同一个华为账号登录、并一个应用包名、同一个网络之间进行,也可以两个设备同时开启蓝牙。

开发指南

1. 在config.json中添加permisssion权限。

// 添加在abilities同一目录层级 
"reqPermissions": [ 
    { 
        "name""ohos.permission.DISTRIBUTED_DATASYNC" 
    } 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.

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); 
 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.

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; 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.

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(); 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.

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(); 
    }); 
  } 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.

6.获取分布式数据库数据

private void flushUIData() { 
  //查询分布式数据的数据,获取数据可以通过get(String key)/ getEntries(String key)方法获取数据 
  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"); 
  } 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.

7. 解除订阅。一般在页面销毁时调用,也就是MainAbilitySlice的onStop()中调用

if (singleKvStore != null) { 
  singleKvStore.unSubscribe(innerKvStoreObserver); 

  • 1.
  • 2.
  • 3.

8. 同步数据到其他设备。获取已连接的设备列表,选择同步方式进行数据同步

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); 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.

项目中采用在后台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<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); 
   //删除数据库 
   DBUtils.clearDB(); 
    //解除订阅 
    if (singleKvStore != null) { 
      singleKvStore.unSubscribe(innerKvStoreObserver); 
    } 
  } 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.

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(); 
      } 
 
    } 
  } 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.

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); 
  } 
 
 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.

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); 
  } 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.

5. MyApplication

public class MyApplication extends AbilityPackage { 
 
  @Override 
  public void onInitialize() { 
    super.onInitialize(); 
  } 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.

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" 
      } 
    ] 
  } 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.

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> 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.

想了解更多内容,请访问:

51CTO和华为官方合作共建的鸿蒙技术社区

https://harmonyos.51cto.com