#星光计划2.0#HarmonyOS分布式应用农业大棚数据监测解读

Posted HarmonyOS技术社区

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了#星光计划2.0#HarmonyOS分布式应用农业大棚数据监测解读相关的知识,希望对你有一定的参考价值。

【本文正在参与51CTO HarmonyOS技术社区创作者激励计划-星光计划2.0】

引言

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

功能介绍

此次基于HarmonyOS的分布式数据服务能力,一方面模拟农业大棚的温度、湿度、二氧化碳浓度等数据的采集,并在手机端进行采集数据展示;另一方面,可以把手机端的数据,迁移到其他设备(如智慧屏、手表、PAD等),可以做一些数据分析展示。

前提:

在不同设备之间,要实现分布式数据服务的同步能力,需要同一个华为账号登录、并一个应用包名、同一个网络之间进行,也可以是两个设备同时开启蓝牙。

开发指南

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); //自动同步为true,手动同步为false
  singleKvStore = kvManager.getKvStore(options, storeId);
  return singleKvStore;

4.将数据写入单版本分布式数据库

//以key-value形式存储到分布式数据库
try 
  //将采集的数据以key-value形式存入分布式数据库中
  DataModle dataModle=new DataModle();
  dataModle.setTemp(temp);
  dataModle.setHumi(humi);
  dataModle.setCo2(co2);
  String jsonString= ZSONObject.toZSONString(dataModle);
  singleKvStore.putString("data",jsonString);
 catch (KvStoreException e) 
  LogUtils.debug(TAG, "DataServiceAbility::updateData()"+e.getMessage());

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.获取分布式数据库数据

//查询分布式数据的数据,获取数据可以通过get(String key)或者 getEntries(String key)方法获取数据
List<Entry> entries = singleKvStore.getEntries("data");
if (entries.size() > 0)
ZSONObject zsonObject = ZSONObject.stringToZSON(entries.get(0).getValue().getString());
double temp = zsonObject.getDouble("temp");
double humi = zsonObject.getDouble("humi");
double co2 = zsonObject.getDouble("co2");
String strTemp = String.format("%.1f", temp);
String strHumi = String.format("%.1f", humi);
String strCO2 = String.format("%.1f", co2);
tvTemp.setText(strTemp+"℃");
tvHumi.setText(strHumi+"%RH");
tvCo2.setText(strCO2+"ppm");

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

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

## 8.同步数据到其他设备。获取已连接的设备列表,选择同步方式进行数据同步
```//查看在线设备
List<DeviceInfo> onlineDevices = DeviceManager
    .getDeviceList(DeviceInfo.FLAG_GET_ONLINE_DEVICE);
List<String> deviceIdList = new ArrayList<>();
for (DeviceInfo deviceInfo : deviceInfoList) 
    deviceIdList.add(deviceInfo.getId());

//迁移到指定设备,传入设备ID列表
singleKvStore.sync(deviceIdList, SyncMode.PUSH_ONLY);

项目中采用在后台service中开启定时任务,模拟农业大棚采集数据,实时保存数据到分布式数据库,然后在主界面,监听数据变化,实时更新数据。当选择迁移的设备时,就可以把数据迁移到相应设备。

手机侧应用刚打开时界面

TV侧应用刚打开时界面

点击右上角迁移按钮,并选择迁移设备(P40-0036)


迁移后的左侧设备数据和右侧设备数据就会同步一致

附上源码

手机端

1. PhoneAbilitySlice

public class PhoneAbilitySlice extends AbilitySlice 
  private SingleKvStore singleKvStore;
  private InnerKvStoreObserver innerKvStoreObserver;
  private Intent serviceIntent;
  private Text tvTemp;
  private Text tvHumi;
  private Text tvCo2;
  private DeviceData chooseDevice;
  private DevicesProvider devicesProvider;
  private CommonDialog commonDialog;
  private String TAG="MainAbilitySlice";
  private List<String>deviceIdList;
  @Override
  public void onStart(Intent intent) 
    super.onStart(intent);
    super.setUIContent(ResourceTable.Layout_ability_main);
    //设置沉浸式状态栏  getWindow().addFlags(WindowManager.LayoutConfig.MARK_TRANSLUCENT_STATUS);
    initView();
    initService();
    try 
      //获取数据库
      singleKvStore = DBUtils.initOrGetDB(this, DBUtils.KV_STORE_NAME);
      innerKvStoreObserver = new InnerKvStoreObserver();
      //订阅分布式数据库
      singleKvStore.subscribe(SubscribeType.SUBSCRIBE_TYPE_ALL, innerKvStoreObserver);
     catch (KvStoreException e) 
      LogUtils.debug(TAG, "MainAbilitySlice::onStart/"+e.getMessage());
    
  
  private void initService() 
    //启动ServiceAbility
    serviceIntent = new Intent();
    Operation operation = new Intent.OperationBuilder()
        .withDeviceId("")
        .withBundleName("com.isoftstone.distributeddata")
        .withAbilityName("com.isoftstone.distributeddata.DataServiceAbility")
        .build();
    serviceIntent.setOperation(operation);
    startAbility(serviceIntent);
  
  private void initView() 
    tvTemp = (Text) findComponentById(ResourceTable.Id_text_temp);
    tvHumi = (Text) findComponentById(ResourceTable.Id_text_humi);
    tvCo2 = (Text) findComponentById(ResourceTable.Id_text_co2);
    Button bt = (Button) findComponentById(ResourceTable.Id_bt_continue);
    bt.setClickedListener(component -> 
      if (component.getId() == ResourceTable.Id_bt_continue) 
        //查看在线设备
        List<DeviceInfo> onlineDevices = DeviceManager
            .getDeviceList(DeviceInfo.FLAG_GET_ONLINE_DEVICE);
        List<DeviceData> deviceDatas = new ArrayList<>();
        if (onlineDevices == null || onlineDevices.size() < 1) 
          CustomerDialog.showToastDialog(getAbility(), "无组网在线设备");
         else 
          for (DeviceInfo deviceInfo : onlineDevices) 
            deviceDatas.add(new DeviceData(false, deviceInfo));
          
          showDevices(deviceDatas);
        
      
    );
  
  private void showDevices(List<DeviceData> deviceDatas) 
    chooseDevice = null;
    commonDialog = new CommonDialog(this);
    Component component = LayoutScatter.getInstance(this)
        .parse(ResourceTable.Layout_dialog_layout_device, null, true);
    ListContainer listContainer = (ListContainer) component.findComponentById(ResourceTable.Id_list_container_device);
    devicesProvider = new DevicesProvider(this, deviceDatas);
    listContainer.setItemProvider(devicesProvider);
    listContainer.setItemClickedListener((listContainer1, component1, position, l) -> 
      chooseDevice = deviceDatas.get(position);
      for (int i = 0; i < deviceDatas.size(); i++) 
        if (i == position) 
          deviceDatas.set(i, new DeviceData(true, deviceDatas.get(i).getDeviceInfo()));
         else 
          deviceDatas.set(i, new DeviceData(false, deviceDatas.get(i).getDeviceInfo()));
        
      
      devicesProvider = new DevicesProvider(this, deviceDatas);
      listContainer1.setItemProvider(devicesProvider);
    );
    Text tvCancle = (Text) component.findComponentById(ResourceTable.Id_operate_no);
    Text tvSure = (Text) component.findComponentById(ResourceTable.Id_operate_yes);

    tvCancle.setClickedListener(component12 -> commonDialog.destroy());

    tvSure.setClickedListener(component13 -> 
      if (chooseDevice == null) 
        CustomerDialog.showToastDialog(this, "请选择设备");
       else 
        try 
          deviceIdList=new ArrayList<>();
          deviceIdList.add(chooseDevice.getDeviceInfo().getDeviceId());
          //手动同步的设备列表
          singleKvStore.sync(deviceIdList, SyncMode.PUSH_ONLY);
          commonDialog.destroy();
         catch (IllegalStateException e) 
          //流转异常捕获,防止进程存在再次发起流转
          LogUtils.debug(TAG, "MainAbilitySlice::singleKvStore.sync()/"+e.getMessage());
        
      
    );
    commonDialog.setSize(MATCH_PARENT, MATCH_CONTENT);
    commonDialog.setAlignment(LayoutAlignment.BOTTOM);
    commonDialog.setCornerRadius(10);
    commonDialog.setAutoClosable(true);
    commonDialog.setContentCustomComponent(component);
    commonDialog.setTransparent(true);
    commonDialog.show();
  
  public class InnerKvStoreObserver implements KvStoreObserver 

    @Override
    public void onChange(ChangeNotification changeNotification) 
      //onChange方法实质上,在一个子线程里执行
      getUITaskDispatcher().asyncDispatch(() -> 
        //在这里执行页面ui组件的显示刷新
        asyncUpdateData();
      );
    
  
  public void asyncUpdateData()
    //查询分布式数据的数据
    List<Entry> entries = singleKvStore.getEntries("data");
    if (entries.size() > 0) 
      ZSONObject zsonObject = ZSONObject.stringToZSON(entries.get(0).getValue().getString());
      double temp = zsonObject.getDouble("temp");
      double humi = zsonObject.getDouble("humi");
      double co2 = zsonObject.getDouble("co2");
      String strTemp = String.format("%.1f", temp);
      String strHumi = String.format("%.1f", humi);
      String strCO2 = String.format("%.1f", co2);
      tvTemp.setText(strTemp+"℃");
      tvHumi.setText(strHumi+"%RH");
      tvCo2.setText(strCO2+"ppm");
      //手动同步的设备列表
      if(singleKvStore!=null)
        if(deviceIdList!=null&&deviceIdList.size()>0)
          singleKvStore.sync(deviceIdList, SyncMode.PUSH_ONLY);
        
      
    
  

  @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) 
      if(innerKvStoreObserver!=null)
        singleKvStore.unSubscribe(innerKvStoreObserver);
      
    
  

2. DataServiceAbility

public class DataServiceAbility extends Ability 
  private static final HiLogLabel LABEL_LOG = new HiLogLabel(3, 0xD001100, "Demo");
  private SingleKvStore singleKvStore;
  private NotificationRequest request;
  private Timer mTimer;
  private TimerTask mTimerTask;
  private String TAG="DataServiceAbility";
  @Override
  public void onStart(Intent intent) 
    LogUtils.debug(TAG, "DataServiceAbility::onStart");
    super.onStart(intent);
    //创建前台service
    createForeService();
    try 
      singleKvStore = DBUtils.initOrGetDB(this, DBUtils.KV_STORE_NAME);
     catch (Exception e) 
      LogUtils.debug(TAG, "DataServiceAbility::onStart()"+e.getMessage());
    
    //每隔10秒,模拟传感器采集一次大棚数据。
    if(mTimer==null)
      mTimer=new Timer();
    
    if(mTimerTask==null)
      mTimerTask=new TimerTask() 
        @Override
        public void run() 
          updateData();
        
      ;
    
    mTimer.schedule(mTimerTask,0,1000*10);
  
  private void updateData() 
    //获取随机温度0-100;
    double temp= new Random().nextDouble()*100;
    //获取随机湿度0-100;
    double humi= new Random().nextDouble()*100;
    //获取随机CO2浓度0-1000
    double co2= new Random().nextDouble()*1000;
    try 
      //将采集的数据以key-value形式存入分布式数据库中
      DataModle dataModle=new DataModle();
      dataModle.setTemp(temp);
      dataModle.setHumi(humi);
      dataModle.setCo2(co2);
      String jsonString= ZSONObject.toZSONString(dataModle);
      singleKvStore.putString("data",jsonString);
     catch (KvStoreException e) 
      LogUtils.debug(TAG, "DataServiceAbility::updateData()"+e.getMessage());
    
  
  private void createForeService() 
    // 创建通知,其中1005为notificationId
    request = new NotificationRequest(1005);
    NotificationRequest.NotificationNormalContent content = new NotificationRequest.NotificationNormalContent();
    content.setTitle("农业大棚").setText("数据采集服务开启中");
    NotificationRequest.NotificationContent notificationContent = new NotificationRequest.NotificationContent(content);
    request.setContent(notificationContent);
    //绑定通知,1005为创建通知时传入的notificationId
    keepBackgroundRunning(1005, request);
  
  @Override
  public void onBackground() 
    super.onBackground();
    LogUtils.debug(TAG, "DataServiceAbility::onBackground()");
  

  @Override
  public void onStop() 
    super.onStop();
    if (mTimerTask != null) 
      mTimerTask.cancel();
      mTimerTask=null;
     if (mTimer != null) 
      mTimer.cancel();
      mTimer=null;
    
    //停止前台Service。
    cancelBackgroundRunning();
    LogUtils.debug(TAG, "DataServiceAbility::onStop()");
  

  @Override
  public void onCommand(Intent intent, boolean restart, int startId) 
  
  @Override
  public IRemoteObject onConnect(Intent intent) 
    return null;
  
  @Override
  public void onDisconnect(Intent intent) 
  

3.DBUtils

public class DBUtils 
  private static KvManager kvManager;
  private static SingleKvStore singleKvStore;
  public static String KV_STORE_NAME="farm_data";
  //具体的实现数据库的初始化
  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(false);//自动同步为true,手动同步为false
    singleKvStore = kvManager.getKvStore(options, storeId);
    return singleKvStore;
  
  // 如果数据库中的字段有修改,只能先关闭,后删除,然后重新创建才生效
  public static void clearDB() 
    kvManager.closeKvStore(singleKvStore);
    kvManager.deleteKvStore(KV_STORE_NAME);
  

4. MainAbility

```public class MainAbility extends Ability br/>@Override
public void onStart(Intent intent)
super.onStart(intent);
super.setMainRoute(PhoneAbilitySlice.class.getName());
//实现Ability的代码中显式声明需要使用多设备协同访问的权限
requestPermissionsFromUser(new String[]"ohos.permission.DISTRIBUTED_DATASYNC",, 0);

### 5. DataModle

public class DataModle
private double temp;
private double humi;
private double co2;
public double getTemp()
return temp;

public void setTemp(double temp)
this.temp = temp;

public double getHumi()
return humi;

public void setHumi(double humi)
this.humi = humi;

public double getCo2()
return co2;

public void setCo2(double co2)
this.co2 = co2;

## 6. DeviceData

public class DeviceData
private boolean isChecked;
private DeviceInfo deviceInfo;
/**

### 10. config.json 文件
```
  "app": 
    "bundleName": " com.isoftstone.distributeddata ",
    "vendor": "isoftstone",
    "version": 
      "code": 1000000,
      "name": "1.0.0"
    
  ,
  "deviceConfig": ,
  "module": 
    "package": "com.isoftstone.distributeddata",
    "name": ".MyApplication",
    "mainAbility": "com.isoftstone.distributeddata.MainAbility",
    "deviceType": [
      "phone"
    ],
    "distro": 
      "deliveryWithInstall": true,
      "moduleName": "entry",
      "moduleType": "entry",
      "installationFree": false
    ,
    "reqPermissions": [
      
        "name": "ohos.permission.DISTRIBUTED_DATASYNC"
      ,
      
//添加此权限,才能查找分布式在线设备
        "name": "ohos.permission.GET_DISTRIBUTED_DEVICE_INFO"
      ,
      
//将service设置为前台服务,需要添加的权限
        "name": "ohos.permission.KEEP_BACKGROUND_RUNNING" 
      
    ],
    "abilities": [
      
        "skills": [
          
            "entities": [
              "entity.system.home"
            ],
            "actions": [
              "action.system.home"
            ]
          
        ],
        "orientation": "unspecified",
        "name": "com.isoftstone.distributeddata.MainAbility",
        "icon": "$media:icon",
        "description": "$string:mainability_description",
        "label": "$string:entry_MainAbility",
        "type": "page",
        "launchType": "standard"
      ,
      
        "name": "com.isoftstone.distributeddata.DataServiceAbility",
        "icon": "$media:icon",
        "description": "$string:dataserviceability_description",
        "type": "service",
        "visible": true,
        "backgroundModes": [
          "dataTransfer",
          "location"
        ]
      

    ],
    "metaData": 
      "customizeData": [
        
          "name": "hwc-theme",
          "value": "androidhwext:style/Theme.Emui.NoTitleBar",
          "extra": ""
        
      ]
    
  

11.ability_main.xml布局文件

<?xml version="1.0" encoding="utf-8"?>
<DirectionalLayout
  xmlns:ohos="http://schemas.huawei.com/res/ohos"
  ohos:
  ohos:
  ohos:orientation="vertical">

  <DependentLayout
    ohos:
    ohos:
    ohos:orientation="horizontal"
    ohos:top_padding="50vp"
    ohos:bottom_padding="20vp"
    ohos:left_padding="20vp"
    ohos:right_padding="20vp"
    ohos:background_element="$graphic:background_ability_main">
    <Text
      ohos:
      ohos:
      ohos:text="农业大棚数据监测"
      ohos:text_size="26vp"
      ohos:text_color="#ffffff"
      ohos:center_in_parent="true"/>
    <Button
      ohos:id="$+id:bt_continue"
      ohos:
      ohos:
      ohos:background_element="$media:conti"
      ohos:align_parent_right="true"
      ohos:vertical_center="true"/>
  </DependentLayout>
  <DependentLayout
    ohos:
    ohos:
    ohos:background_element="#f4f5f7"
    ohos:left_padding="15vp"
    ohos:right_padding="15vp"
    ohos:top_padding="10vp"
    ohos:bottom_padding="10vp">

    <Text
      ohos:
      ohos:
      ohos:text="温度:"
      ohos:text_color="#000000"
      ohos:text_size="18fp"
      ohos:vertical_center="true"/>

    <Text
      ohos:id="$+id:text_temp"
      ohos:
      ohos:
      ohos:text="数据正在采集 "
      ohos:text_color="#00ff00"
      ohos:text_size="18fp"
      ohos:left_margin="50vp"
      ohos:vertical_center="true"/>
  </DependentLayout>

  <Component
    ohos:
    ohos:
    ohos:background_element="#FFFFFF"/>

  <DependentLayout
    ohos:
    ohos:
    ohos:background_element="#f4f5f7"
    ohos:left_padding="15vp"
    ohos:right_padding="15vp"
    ohos:top_padding="10vp"
    ohos:bottom_padding="10vp">

    <Text
      ohos:
      ohos:
      ohos:text="湿度:"
      ohos:text_color="#000000"
      ohos:text_size="18fp"
      ohos:vertical_center="true"/>

    <Text
      ohos:id="$+id:text_humi"
      ohos:
      ohos:
      ohos:text="数据正在采集 "
      ohos:text_color="#00ff00"
      ohos:text_size="18fp"
      ohos:left_margin="50vp"
      ohos:vertical_center="true"/>
  </DependentLayout>

  <Component
    ohos:
    ohos:
    ohos:background_element="#FFFFFF"/>

  <DependentLayout
    ohos:
    ohos:
    ohos:background_element="#f4f5f7"
    ohos:left_padding="15vp"
    ohos:right_padding="15vp"
    ohos:top_padding="10vp"
    ohos:bottom_padding="10vp">

    <Text
      ohos:
      ohos:
      ohos:text="CO2:"
      ohos:text_color="#000000"
      ohos:text_size="18fp"
      ohos:vertical_center="true"/>

    <Text
      ohos:id="$+id:text_co2"
      ohos:
      ohos:
      ohos:text="数据正在采集 "
      ohos:text_color="#00ff00"
      ohos:text_size="18fp"
      ohos:left_margin="50vp"
      ohos:vertical_center="true"/>
  </DependentLayout>

</DirectionalLayout>

12. dialog_device_item.xml

<?xml version="1.0" encoding="utf-8"?>
<DirectionalLayout
    xmlns:ohos="http://schemas.huawei.com/res/ohos"
    ohos:
    ohos:
    ohos:orientation="horizontal">

    <Image
        ohos:id="$+id:item_type"
        ohos:
        ohos:
        ohos:image_src="$media:dv_phone"
        ohos:layout_alignment="vertical_center"
        ohos:left_margin="10vp"
        ohos:scale_mode="inside"/>

    <Text
        ohos:id="$+id:item_name"
        ohos:
        ohos:
        ohos:max_text_lines="1"
        ohos:text="Huawei P40"
        ohos:text_alignment="vertical_center"
        ohos:text_size="15fp"
        ohos:weight="1"/>

    <Image
        ohos:id="$+id:item_check"
        ohos:
        ohos:
        ohos:image_src="$media:uncheck2"
        ohos:layout_alignment="vertical_center"
        ohos:right_margin="15vp"/>
</DirectionalLayout>

13.dialog_layout_device.xml

```<?xml version="1.0" encoding="utf-8"?>
<DirectionalLayout
xmlns:ohos="http://schemas.huawei.com/res/ohos"
ohos:height="340vp"
ohos:width="match_parent"
ohos:orientation="vertical">

<DirectionalLayout
    ohos:
    ohos:
    ohos:background_element="$graphic:background_white_radius_10"
    ohos:bottom_margin="50vp"
    ohos:left_margin="20vp"
    ohos:orientation="vertical"
    ohos:right_margin="20vp">

    <Text
        ohos:
        ohos:
        ohos:left_padding="20vp"
        ohos:text="迁移到其他设备"
        ohos:text_alignment="vertical_center"
        ohos:text_color="#000000"
        ohos:text_size="16fp"/>

    <ListContainer
        ohos:id="$+id:list_container_device"
        ohos:
        ohos:
        ohos:weight="1"/>

    <DirectionalLayout
        ohos:
        ohos:
        ohos:orientation="horizontal">

        <Text
            ohos:id="$+id:operate_no"
            ohos:
            ohos:
            ohos:text="取消"
            ohos:text_alignment="center"
            ohos:text_color="#1e90ff"
            ohos:text_size="17fp"
            ohos:weight="1"/>

        <Component
            ohos:
            ohos:
            ohos:background_element="#cccccc"
            ohos:layout_alignment="vertical_center"/>

        <Text
            ohos:id="$+id:operate_yes"
            ohos:
            ohos:
            ohos:text="确定"
            ohos:text_alignment="center"
            ohos:text_color="#1e90ff"
            ohos:text_size="17fp"
            ohos:weight="1"/>
    </DirectionalLayout>
</DirectionalLayout>

</DirectionalLayout>

### 14. toast_dialog.xml
```<?xml version="1.0" encoding="utf-8"?>
<DirectionalLayout
    xmlns:ohos="http://schemas.huawei.com/res/ohos"
    ohos:
    ohos:
    ohos:orientation="vertical"
    ohos:background_element="$graphic:background_gray_circle">
    <Text
        ohos:id="$+id:toast"
        ohos:
        ohos:
        ohos:left_padding="16vp"
        ohos:right_padding="16vp"
        ohos:top_padding="4vp"
        ohos:bottom_padding="4vp"
        ohos:layout_alignment="center"
        ohos:text_size="20fp"/>
</DirectionalLayout>

TV端

1. TVAbilitySlice

```public class TVAbilitySlice extends AbilitySlice

private Text tvTemp;
private Text tvTempMax;
private Text tvTempMin;

private Text tvHumi;
private Text tvHumiMax;
private Text tvHumiMin;

private Text tvCgas;
private Text tvCgasMax;
private Text tvCgasMin;

private Text tvTempStatus;
private Text tvHumiStatus;
private Text tvCgasStatus;

private ProgressBar rgbTem;
private ProgressBar rgbHumi;
private ProgressBar rgbCgas;

private static final HiLogLabel label = new HiLogLabel(HiLog.LOG_APP, 0x0001, "my_log");

private double temp;
private double humi;
private double cGas;
private SingleKvStore singleKvStore;
private KvStoreObserverClient kvStoreObserverClient;

@Override
public void onStart(Intent intent) 
    super.onStart(intent);
    super.setUIContent(ResourceTable.Layout_ability_tv);
    //设置沉浸式状态栏
    getWindow().addFlags(WindowManager.LayoutConfig.MARK_TRANSLUCENT_STATUS);

    tvTemp = (Text) findComponentById(ResourceTable.Id_tvTemp);
    tvTempMax = (Text) findComponentById(ResourceTable.Id_tvMaxTemp);
    tvTempMin = (Text) findComponentById(ResourceTable.Id_tvMinTemp);
    rgbTem = (RoundProgressBar) findComponentById(ResourceTable.Id_rgb_tem);

    tvHumi = (Text) findComponentById(ResourceTable.Id_tvHumi);
    tvHumiMax = (Text) findComponentById(ResourceTable.Id_tvMaxHumi);
    tvHumiMin = (Text) findComponentById(ResourceTable.Id_tvMinHumi);
    rgbHumi = (RoundProgressBar) findComponentById(ResourceTable.Id_rgb_humi);

    tvCgas = (Text) findComponentById(ResourceTable.Id_tvCgas);
    tvCgasMax = (Text) findComponentById(ResourceTable.Id_tvMaxCgas);
    tvCgasMin = (Text) findComponentById(ResourceTable.Id_tvMinCgas);
    rgbCgas = (RoundProgressBar) findComponentById(ResourceTable.Id_rgb_gas);

    tvTempStatus = (Text) findComponentById(ResourceTable.Id_tvTempStatus);
    tvHumiStatus = (Text) findComponentById(ResourceTable.Id_tvHumiStatus);
    tvCgasStatus = (Text) findComponentById(ResourceTable.Id_tvCgasStatus);

    try 
        KvManagerConfig config = new KvManagerConfig(getContext());
        KvManager kvManager = KvManagerFactory.getInstance().createKvManager(config);
        Options CREATE = new Options();
        CREATE.setCreateIfMissing(true).setEncrypt(false)
            .setKvStoreType(KvStoreType.SINGLE_VERSION)
            .setAutoSync(true);
        singleKvStore = kvManager.getKvStore(CREATE, DBUtils.KV_STORE_NAME);
        kvStoreObserverClient = new KvStoreObserverClient();
        singleKvStore.subscribe(SubscribeType.SUBSCRIBE_TYPE_ALL, kvStoreObserverClient);
     catch (Exception e) 
        e.printStackTrace();
    



private class KvStoreObserverClient implements KvStoreObserver 

    @Override
    public void onChange(ChangeNotification notification) 
        //onChange方法实质上,在一个子线程里执行
        getUITaskDispatcher().asyncDispatch(() -> 
            //在这里执行页面ui组件的显示刷新
            asyncUpdateData();
        );
    


public void asyncUpdateData()
    //查询分布式数据的数据
    List<Entry> entries = singleKvStore.getEntries("data");
    if (entries.size() > 0) 
        ZSONObject zsonObject = ZSONObject.stringToZSON(entries.get(0).getValue().getString());
        double temp = zsonObject.getDouble("temp");
        double humi = zsonObject.getDouble("humi");
        double co2 = zsonObject.getDouble("co2");
        String strTemp = String.format("%.1f", temp);
        String strHumi = String.format("%.1f", humi);
        String strCO2 = String.format("%.1f", co2);
        initView(strTemp,strHumi,strCO2);

    


private void initView(String strTemp,String strHumi,String strCO2) 
    temp = Double.valueOf(strTemp);
    int tempMax = 45;
    int tempMin = -10;

    humi = Double.valueOf(strHumi);
    int humiMax = 70;
    int humiMin = 10;

    cGas = Double.valueOf(strCO2);
    int cGasMax = 1000;

    if (temp > -100) 
        if (temp > tempMax || temp < tempMin) 
            tvTemp.setTextColor(Color.RED);
            tvTempStatus.setText("异常");
            tvTempStatus.setTextColor(Color.RED);
            rgbTem.setProgressColor(Color.RED);
         else 
            tvTemp.setTextColor(Color.GREEN);
            tvTempStatus.setText("正常");
            tvTempStatus.setTextColor(Color.GREEN);
            rgbTem.setProgressColor(Color.GREEN);

        
     else 
        tvTemp.setTextColor(Color.BLACK);
        tvTempStatus.setTextColor(Color.BLACK);
        tvTempStatus.setText("未知");
        rgbTem.setProgressColor(Color.GREEN);

    
    tvTempMax.setText(tempMax + "℃");
    tvTempMin.setText(tempMin + "℃");

    if (humi > -100) 
        if (humi > humiMax || humi < humiMin) 
            tvHumi.setTextColor(Color.RED);
            tvHumiStatus.setText("异常");
            tvHumiStatus.setTextColor(Color.RED);
            rgbHumi.setProgressColor(Color.RED);
         else 
            tvHumi.setTextColor(Color.GREEN);
            tvHumiStatus.setText("正常");
            tvHumiStatus.setTextColor(Color.GREEN);
            rgbHumi.setProgressColor(Color.GREEN);
        
     else 
        tvHumi.setTextColor(Color.BLACK);
        tvHumiStatus.setText("未知");
        tvHumiStatus.setTextColor(Color.BLACK);
        rgbHumi.setProgressColor(Color.GREEN);

    
    tvHumiMax.setText(humiMax + "% RH");
    tvHumiMin.setText(humiMin + "% RH");

    if (cGas > -100) 
        if (cGas > cGasMax) 
            tvCgas.setTextColor(Color.RED);
            tvCgasStatus.setText("异常");
            tvCgasStatus.setTextColor(Color.RED);
            rgbCgas.setProgressColor(Color.RED);
         else 
            tvCgas.setTextColor(Color.GREEN);
            tvCgasStatus.setText("正常");
            tvCgasStatus.setTextColor(Color.GREEN);
        
     else 
        tvCgas.setTextColor(Color.BLACK);
        tvCgasStatus.setText("未知");
        tvCgasStatus.setTextColor(Color.BLACK);
        rgbCgas.setProgressColor(Color.GREEN);

    

    tvCgasMax.setText(cGasMax + " ppm");

    tvCgasMin.setText(0 + " ppm");

    if (temp <= -100) 
        tvTemp.setText("未知");
        rgbTem.setProgressValue(0);
     else 
        tvTemp.setText(temp + "℃");
        rgbTem.setProgressValue((int) temp);
    
    if (humi <= -100) 
        tvHumi.setText("未知");
        rgbHumi.setProgressValue(0);
     else 
        tvHumi.setText(humi + "% RH");
        rgbHumi.setProgressValue((int) humi);
    
    if (cGas <= -100) 
        tvCgas.setText("未知");
        rgbCgas.setProgressValue(0);
     else 
        tvCgas.setText(cGas + " ppm");
        rgbCgas.setProgressValue((int) cGas);
    



@Override
protected void onStop() 
    super.onStop();
    //解除订阅

// if (singleKvStore != null)
// if(kvStoreObserverClient!=null)
// singleKvStore.unSubscribe(kvStoreObserverClient);
//
//

### 2. TVAbility

public class TVAbility extends Ability

@Override
public void onStart(Intent intent)
super.onStart(intent);
super.setMainRoute(TVAbilitySlice.class.getName());
//实现Ability的代码中显式声明需要使用多设备协同访问的权限
requestPermissionsFromUser(new String[]"ohos.permission.DISTRIBUTED_DATASYNC",, 0);

### 3. ability_tv.xml

<?xml version="1.0" encoding="utf-8"?>
<DirectionalLayout
xmlns:ohos="http://schemas.huawei.com/res/ohos"
ohos:height="match_parent"
ohos:width="match_parent"
ohos:orientation="vertical"
ohos:background_element="$media:haibao">

<Text
ohos:height="match_content"
ohos:width="match_content"
ohos:text="大棚数据监测"
ohos:text_color="#ffffff"
ohos:text_size="26vp"
ohos:layout_alignment="center"
ohos:top_margin="50vp"/>

<DependentLayout
ohos:height="match_parent"
ohos:width="match_parent">

<DirectionalLayout
  ohos:
  ohos:
  ohos:left_padding="10vp"
  ohos:right_padding="10vp"
  ohos:center_in_parent="true"
  ohos:orientation="horizontal">
  <DirectionalLayout
    ohos:
    ohos:
    ohos:weight="1"
    ohos:layout_alignment="center"
    ohos:orientation="vertical">
    <Text
      ohos:
      ohos:
      ohos:text="温度数据"
      ohos:text_size="20vp"
      ohos:padding="5vp"
      ohos:text_color="#ffffff"
      ohos:layout_alignment="center"/>
    <DirectionalLayout
      ohos:
      ohos:
      ohos:background_element="#ffffff"
      ohos:top_margin="15vp"
      ohos:left_margin="20vp"
      ohos:right_margin="20vp"
      ohos:layout_alignment="center"
      ohos:bottom_margin="15vp"
      ohos:visibility="invisible"/>
    <DirectionalLayout
      ohos:
      ohos:
      ohos:layout_alignment="center"
      ohos:orientation="horizontal">
      <DirectionalLayout
        ohos:
        ohos:
        ohos:orientation="vertical">
        <Text
          ohos:
          ohos:
          ohos:text="最高温度阀值:"
          ohos:text_size="16vp"
          ohos:weight="1"
          ohos:text_color="#000000"/>
        <Text
          ohos:
          ohos:
          ohos:text="当前温度:"
          ohos:text_size="16vp"
          ohos:weight="1"
          ohos:text_color="#000000"/>
        <Text
          ohos:
          ohos:
          ohos:text="最低温度阀值:"
          ohos:text_size="16vp"
          ohos:weight="1"
          ohos:text_color="#000000"/>
      </DirectionalLayout>

      <DependentLayout
        ohos:
        ohos:>
        <RoundProgressBar
          ohos:id="$+id:rgb_tem"
          ohos:
          ohos:
          ohos:progress_
          ohos:progress="0"
          ohos:max="100"
          ohos:start_angle="215"
          ohos:max_angle="290"
          ohos:progress_color="#00ff00"
          ohos:center_in_parent="true"/>
        <Text
          ohos:id="$+id:tvMaxTemp"
          ohos:
          ohos:
          ohos:text_size="15vp"
          ohos:text_color="#000000"
          ohos:align_parent_top="true"
          ohos:text="未知"
          ohos:top_margin="8vp"
          ohos:horizontal_center="true"/>
        <Text
          ohos:id="$+id:tvTemp"
          ohos:
          ohos:
          ohos:text_size="15vp"
          ohos:text_color="#000000"
          ohos:text="未知"
          ohos:center_in_parent="true"/>
        <Text
          ohos:id="$+id:tvMinTemp"
          ohos:
          ohos:
          ohos:text_size="14vp"
          ohos:text_color="#000000"
          ohos:text="未知"
          ohos:bottom_margin="8vp"
          ohos:align_parent_bottom="true"
          ohos:horizontal_center="true"/>
      </DependentLayout>

    </DirectionalLayout>

    <DirectionalLayout
      ohos:
      ohos:
      ohos:background_element="#ffffff"
      ohos:top_margin="15vp"
      ohos:left_margin="20vp"
      ohos:right_margin="20vp"
      ohos:layout_alignment="center"
      ohos:bottom_margin="15vp"
      ohos:visibility="invisible"/>
    <DirectionalLayout
      ohos:
      ohos:
      ohos:layout_alignment="center"
      ohos:orientation="horizontal">
      <Text
        ohos:
        ohos:
        ohos:text="温度状态:"
        ohos:text_size="18vp"
        ohos:text_color="#000000"/>
      <Text
        ohos:id="$+id:tvTempStatus"
        ohos:
        ohos:
        ohos:text="未知"
        ohos:text_size="18vp"
        ohos:text_color="#000000"/>
    </DirectionalLayout>
  </DirectionalLayout>

  <DirectionalLayout
    ohos:
    ohos:
    ohos:background_element="#000000"
    ohos:top_margin="6vp"
    ohos:layout_alignment="center"/>

  <DirectionalLayout
    ohos:
    ohos:
    ohos:weight="1"
    ohos:layout_alignment="center"
    ohos:orientation="vertical">
    <Text
      ohos:
      ohos:
      ohos:text="湿度数据"
      ohos:text_size="20vp"
      ohos:padding="5vp"
      ohos:text_color="#ffffff"
      ohos:layout_alignment="center"/>
    <DirectionalLayout
      ohos:
      ohos:
      ohos:background_element="#ffffff"
      ohos:top_margin="15vp"
      ohos:left_margin="20vp"
      ohos:right_margin="20vp"
      ohos:layout_alignment="center"
      ohos:bottom_margin="15vp"
      ohos:visibility="invisible"/>

    <DirectionalLayout
      ohos:
      ohos:
      ohos:layout_alignment="center"
      ohos:orientation="horizontal">
      <DirectionalLayout
        ohos:
        ohos:
        ohos:orientation="vertical">
        <Text
          ohos:
          ohos:
          ohos:text="最大湿度阀值:"
          ohos:text_size="16vp"
          ohos:weight="1"
          ohos:text_color="#000000"/>
        <Text
          ohos:
          ohos:
          ohos:text="当前湿度:"
          ohos:text_size="16vp"
          ohos:weight="1"
          ohos:text_color="#000000"/>
        <Text
          ohos:
          ohos:
          ohos:text="最小湿度阀值:"
          ohos:text_size="16vp"
          ohos:weight="1"
          ohos:text_color="#000000"/>
      </DirectionalLayout>

      <DependentLayout
        ohos:
        ohos:>
        <RoundProgressBar
          ohos:id="$+id:rgb_humi"
          ohos:
          ohos:
          ohos:progress_
          ohos:progress="0"
          ohos:max="100"
          ohos:start_angle="215"
          ohos:max_angle="290"
          ohos:progress_color="#00ff00"
          ohos:center_in_parent="true"/>
        <Text
          ohos:id="$+id:tvMaxHumi"
          ohos:
          ohos:
          ohos:text_size="15vp"
          ohos:text_color="#000000"
          ohos:text="未知"
          ohos:top_margin="8vp"
          ohos:horizontal_center="true"
          ohos:align_parent_top="true"/>
        <Text
          ohos:id="$+id:tvHumi"
          ohos:
          ohos:
          ohos:text_size="15vp"
          ohos:text_color="#000000"
          ohos:text="未知"
          ohos:center_in_parent="true"/>
        <Text
          ohos:id="$+id:tvMinHumi"
          ohos:
          ohos:
          ohos:text_size="15vp"
          ohos:text_color="#000000"
          ohos:horizontal_center="true"
          ohos:text="未知"
          ohos:bottom_margin="8vp"
          ohos:align_parent_bottom="true"/>
      </DependentLayout>

    </DirectionalLayout>

    <DirectionalLayout
      ohos:
      ohos:
      ohos:background_element="#ffffff"
      ohos:top_margin="15vp"
      ohos:left_margin="20vp"
      ohos:right_margin="20vp"
      ohos:layout_alignment="center"
      ohos:bottom_margin="15vp"
      ohos:visibility="invisible"/>
    <DirectionalLayout
      ohos:
      ohos:
      ohos:layout_alignment="center"
      ohos:orientation="horizontal">
      <Text
        ohos:
        ohos:
        ohos:text="湿度状态:"
        ohos:text_size="18vp"
        ohos:text_color="#000000"/>
      <Text
        ohos:id="$+id:tvHumiStatus"
        ohos:
        ohos:
        ohos:text="未知"
        ohos:text_size="18vp"
        ohos:text_color="#000000"/>
    </DirectionalLayout>

  </DirectionalLayout>

  <DirectionalLayout
    ohos:
    ohos:
    ohos:background_element="#000000"
    ohos:top_margin="6vp"
    ohos:layout_alignment="center"/>

  <DirectionalLayout
    ohos:
    ohos:
    ohos:weight="1"
    ohos:layout_alignment="center"
    ohos:orientation="vertical">
    <Text
      ohos:
      ohos:
      ohos:text="CO2数据"
      ohos:text_size="20vp"
      ohos:padding="5vp"
      ohos:text_color="#ffffff"
      ohos:layout_alignment="center"/>
    <DirectionalLayout
      ohos:
      ohos:
      ohos:background_element="#ffffff"
      ohos:top_margin="15vp"
      ohos:left_margin="20vp"
      ohos:right_margin="20vp"
      ohos:layout_alignment="center"
      ohos:bottom_margin="15vp"
      ohos:visibility="invisible"/>
    <DirectionalLayout
      ohos:
      ohos:
      ohos:layout_alignment="center"
      ohos:orientation="horizontal">
      <DirectionalLayout
        ohos:
        ohos:
        ohos:orientation="vertical">
        <Text
          ohos:
          ohos:
          ohos:text="最大气体增量:"
          ohos:text_size="16vp"
          ohos:weight="1"
          ohos:text_color="#000000"/>
        <Text
          ohos:
          ohos:
          ohos:text="当前增量:"
          ohos:text_size="16vp"
          ohos:weight="1"
          ohos:text_color="#000000"/>
        <Text
          ohos:
          ohos:
          ohos:text="最小气体增量:"
          ohos:text_size="16vp"
          ohos:weight="1"
          ohos:text_color="#000000"/>
      </DirectionalLayout>

      <DependentLayout
        ohos:
        ohos:>
        <RoundProgressBar
          ohos:id="$+id:rgb_gas"
          ohos:
          ohos:
          ohos:progress_
          ohos:progress="0"
          ohos:max="1000"
          ohos:start_angle="215"
          ohos:max_angle="290"
          ohos:progress_color="#00ff00"
          ohos:center_in_parent="true"/>
        <Text
          ohos:id="$+id:tvMaxCgas"
          ohos:
          ohos:
          ohos:text_size="15vp"
          ohos:text="未知"
          ohos:text_color="#000000"
          ohos:top_margin="8vp"
          ohos:horizontal_center="true"
          ohos:align_parent_top="true"/>
        <Text
          ohos:id="$+id:tvCgas"
          ohos:
          ohos:
          ohos:text_size="15vp"
          ohos:text="未知"
          ohos:text_color="#000000"
          ohos:center_in_parent="true"/>
        <Text
          ohos:id="$+id:tvMinCgas"
          ohos:
          ohos:
          ohos:text_size="15vp"
          ohos:text="未知"
          ohos:text_color="#000000"
          ohos:bottom_margin="8vp"
          ohos:horizontal_center="true"
          ohos:align_parent_bottom="true"/>
      </DependentLayout>
    </DirectionalLayout>

    <DirectionalLayout
      ohos:
      ohos:
      ohos:background_element="#ffffff"
      ohos:top_margin="15vp"
      ohos:left_margin="20vp"
      ohos:right_margin="20vp"
      ohos:layout_alignment="center"
      ohos:bottom_margin="15vp"
      ohos:visibility="invisible"/>

    <DirectionalLayout
      ohos:
      ohos:
      ohos:layout_alignment="center"
      ohos:orientation="horizontal">
      <Text
        ohos:
        ohos:
        ohos:text="气体状态:"
        ohos:text_size="18vp"
        ohos:text_color="#000000"/>
      <Text
        ohos:id="$+id:tvCgasStatus"
        ohos:
        ohos:
        ohos:text="未知"
        ohos:text_size="18vp"
        ohos:text_color="#000000"/>
    </DirectionalLayout>
  </DirectionalLayout>
</DirectionalLayout>

</DependentLayout>

</DirectionalLayout>

### 4.config.json


"app":
"bundleName": "com.isoftstone. distributeddata ",
"vendor": "isoftstone",
"version":
"code": 1000000,
"name": "1.0.0"

,
"deviceConfig": ,
"module":
"package": "com.isoftstone.distributeddata",
"name": ".MyApplication",
"mainAbility": "com.isoftstone.distributeddata.com.isoftstone.distributeddata.TVAbility",
"deviceType": [
"phone"
],
"distro":
"deliveryWithInstall": true,
"moduleName": "entry",
"moduleType": "entry",
"installationFree": false
,
"reqPermissions": [

"name": "ohos.permission.DISTRIBUTED_DATASYNC"

],
"abilities": [

"skills": [

"entities": [
"entity.system.home"
],
"actions": [
"action.system.home"
]

],
"orientation": "landscape",
"name": "com.isoftstone.distributeddata.TVAbility",
"icon": "$media:icon",
"description": "$string:tvability_description",
"label": "$string:entry_TVAbility",
"type": "page",
"launchType": "standard"

],
"metaData":
"customizeData": [

"name": "hwc-theme",
"value": "androidhwext:style/Theme.Emui.NoTitleBar",
"extra": ""

]



## 更多原创内容请关注:[软通动力HarmonyOS学院](https://harmonyos.51cto.com/column/30)

[想了解更多关于鸿蒙的内容,请访问:](https://harmonyos.51cto.com/#bkwz)

[51CTO和华为官方合作共建的鸿蒙技术社区](https://harmonyos.51cto.com/#bkwz)

https://harmonyos.51cto.com/#bkwz

::: hljs-center

![21_9.jpg](https://s2.51cto.com/images/20210924/1632469265578939.jpg?x-oss-process=image/watermark,size_14,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=)

:::

以上是关于#星光计划2.0#HarmonyOS分布式应用农业大棚数据监测解读的主要内容,如果未能解决你的问题,请参考以下文章

#星光计划2.0#HarmonyOS自定义组件之图层的使用

#星光计划2.0#Harmonyos网络通信真机Demo演练之TCP聊天室

#星光计划2.0#HarmonyOS开发,从listContainer谈容器类控件的使用

#星光计划2.0# OpenHarmony3.0上采用ets开发HAP控制LED灯

#星光计划2.0#基于3861智能开发套件软件开发环境搭建

#星光计划2.0# openHarmony轻松连接华为云物联网平台