想了解更多内容,请访问:
51CTO和华为官方合作共建的鸿蒙技术社区
https://harmonyos.51cto.co
引言分布式数据服务(Distributed Data Service,DDS) 为应用程序提供不同设备间数据库数据分布式的能力。通过调用分布式数据接口,应用程序将数据保存到分布式数据库中。通过结合帐号、应用和数据库三元组,分布式数据服务对属于不同应用的数据进行隔离,保证不同应用之间的数据不能通过分布式数据服务互相访问。在通过可信认证的设备间,分布式数据服务支持应用数据相互同步,为用户提供在多种终端设备上最终一致的数据访问体验。
功能介绍此次基于HarmonyOS的分布式数据服务能力,一方面模拟农业大棚的温度、湿度、二氧化碳浓度等数据的采集,并在手机端进行采集数据展示;另一方面,可以把手机端的数据,迁移到其他设备(如智慧屏、手表、PAD等),可以做一些数据分析展示。
前提:在不同设备之间,要实现分布式数据服务的同步能力,需要同一个华为账号登录、并一个应用包名、同一个网络之间进行,也可以是两个设备同时开启蓝牙。
开发指南 1.在config.json中添加permisssion权限// 添加在abilities同一目录层级
2.在MainAbility中添加权限
- "reqPermissions": [ {
- "name": "ohos.permission.DISTRIBUTED_DATASYNC" }
- ]
3.根据配置构造分布式数据库管理类实例KvManager以及创建分布式数据库对象SingleKvStore
- @Override public void onStart(Intent intent) {
- super.onStart(intent); super.setMainRoute(MainAbilitySlice.class.getName());
- //实现Ability的代码中显式声明需要使用多设备协同访问的权限 requestPermissionsFromUser(new String[]{
- "ohos.permission.DISTRIBUTED_DATASYNC"}, 0);
- }
//实现数据库的初始化
4.将数据写入单版本分布式数据库
- // 初入的参数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; }
5.订阅分布式数据变化。客户端需要实现KvStoreObserver接口,监听数据变化
- //以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()); }
6.获取分布式数据库数据
- //订阅类型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(); });
- } }
7.解除订阅,一般在页面销毁时调用,也就是在onStop()中调用
- //查询分布式数据的数据,获取数据可以通过get(String key)或者 getEntries(String key)方法获取数据 List
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"); }
8.同步数据到其他设备。获取已连接的设备列表,选择同步方式进行数据同步
- if (singleKvStore != null) { singleKvStore.unSubscribe(innerKvStoreObserver);
- }
- List
onlineDevices = DeviceManager .getDeviceList(DeviceInfo.FLAG_GET_ONLINE_DEVICE); - List
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
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
onlineDevices = DeviceManager .getDeviceList(DeviceInfo.FLAG_GET_ONLINE_DEVICE); - List
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
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
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
- @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; public DeviceData(boolean isChecked, DeviceInfo deviceInfo) {
- this.isChecked = isChecked; this.deviceInfo = deviceInfo;
- } public DeviceInfo getDeviceInfo() {
- return deviceInfo; }
- public void setDeviceInfo(DeviceInfo deviceInfo) { this.deviceInfo = deviceInfo;
- } public boolean isChecked() {
- return isChecked; }
- public void setChecked(boolean checked) { isChecked = checked;
- } }
7. DevicesProvider
- public class DevicesProvider extends baseItemProvider {
- private List
data; private LayoutScatter layoutScatter; - public DevicesProvider(Context context, List
data) { - this.data = data; this.layoutScatter = LayoutScatter.getInstance(context);
- }
- @Override public int getCount() {
- return data.size(); }
- @Override
- public Object getItem(int i) { return data.get(i);
- }
- @Override public long getItemId(int i) {
- return i; }
- @Override
- public Component getComponent(int position, Component component, ComponentContainer componentContainer) {
- ViewHolder viewHolder; // component相当于Android中的view,其他的和Android中ListView的适配器adapter差不多。
- // 名字区别也不大,不过Android中ListView基本被淘汰了。 if (component == null) {
- component = layoutScatter.parse(ResourceTable.Layout_dialog_device_item, null, false); viewHolder = new ViewHolder();
- viewHolder.imgType = (Image) component.findComponentById(ResourceTable.Id_item_type); viewHolder.tvName = (Text) component.findComponentById(ResourceTable.Id_item_name);
- viewHolder.imgCheck = (Image) component.findComponentById(ResourceTable.Id_item_check); component.setTag(viewHolder);
- } else { viewHolder = (ViewHolder) component.getTag();
- } DeviceData deviceData = data.get(position);
- DeviceType deviceType=deviceData.getDeviceInfo().getDeviceType(); switch (deviceType){
- case SMART_WATCH: viewHolder.imgType.setPixelMap(ResourceTable.Media_dv_watch);
- break; case SMART_PAD:
- viewHolder.imgType.setPixelMap(ResourceTable.Media_dv_pad); break;
- case SMART_PHONE: viewHolder.imgType.setPixelMap(ResourceTable.Media_dv_phone);
- break; }
- viewHolder.tvName.setText(deviceData.getDeviceInfo().getDeviceName()); if(deviceData.isChecked()){
- viewHolder.imgCheck.setImageAndDecodeBounds(ResourceTable.Media_check2); }else { viewHolder.imgCheck.setImageAndDecodeBounds(ResourceTable.Media_uncheck2); }
- return component; }
- private static class ViewHolder { private Image imgType;
- private Text tvName; private Image imgCheck;
- } }
8. CustomerDialog
- public class CustomerDialog { public static void showToastDialog(Context context, String str) {
- DirectionalLayout toastLayout = (DirectionalLayout) LayoutScatter.getInstance(context) .parse(ResourceTable.Layout_toast_dialog, null, false);
- Text text = (Text) toastLayout.findComponentById(ResourceTable.Id_toast); text.setText(str);
- new ToastDialog(context) .setContentCustomComponent(toastLayout)
- .setSize(DirectionalLayout.LayoutConfig.MATCH_CONTENT, DirectionalLayout.LayoutConfig.MATCH_CONTENT)
- .setAlignment(LayoutAlignment.CENTER) .show();
- } }
9. MyApplication
- @Override public void onInitialize() {
- super.onInitialize(); }
- }
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布局文件
xmlns:ohos="http://schemas.huawei.com/res/ohos" ohos:height="match_parent" - ohos:width="match_parent" ohos:orientation="vertical">
ohos:width="match_parent" ohos:height="match_content" - 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">
ohos:height="match_content" ohos:text="农业大棚数据监测" - ohos:text_size="26vp" ohos:text_color="#ffffff"
- ohos:center_in_parent="true"/>
ohos:height="match_content" ohos:width="match_parent" - ohos:background_element="#f4f5f7" ohos:left_padding="15vp"
- ohos:right_padding="15vp" ohos:top_padding="10vp"
- ohos:bottom_padding="10vp">
ohos:width="match_content" ohos:text="温度:" - ohos:text_color="#000000" ohos:text_size="18fp"
- ohos:vertical_center="true"/>
ohos:height="match_content" ohos:width="match_content" - ohos:text="数据正在采集 " ohos:text_color="#00ff00"
- ohos:text_size="18fp" ohos:left_margin="50vp"
- ohos:vertical_center="true"/>
ohos:height="2vp" ohos:width="match_content" - ohos:background_element="#FFFFFF"/>
ohos:width="match_parent" ohos:background_element="#f4f5f7" - ohos:left_padding="15vp" ohos:right_padding="15vp"
- ohos:top_padding="10vp" ohos:bottom_padding="10vp">
ohos:height="match_content" ohos:width="match_content" - ohos:text="湿度:" ohos:text_color="#000000"
- ohos:text_size="18fp" ohos:vertical_center="true"/>
ohos:id="$+id:text_humi" ohos:height="match_content" - ohos:width="match_content" ohos:text="数据正在采集 "
- ohos:text_color="#00ff00" ohos:text_size="18fp"
- ohos:left_margin="50vp" ohos:vertical_center="true"/>
ohos:width="match_content" ohos:background_element="#FFFFFF"/> ohos:height="match_content" ohos:width="match_parent" - ohos:background_element="#f4f5f7" ohos:left_padding="15vp"
- ohos:right_padding="15vp" ohos:top_padding="10vp"
- ohos:bottom_padding="10vp">
ohos:width="match_content" ohos:text="CO2:" - ohos:text_color="#000000" ohos:text_size="18fp"
- ohos:vertical_center="true"/>
ohos:height="match_content" ohos:width="match_content" - ohos:text="数据正在采集 " ohos:text_color="#00ff00"
- ohos:text_size="18fp" ohos:left_margin="50vp"
- ohos:vertical_center="true"/>
12. dialog_device_item.xml
xmlns:ohos="http://schemas.huawei.com/res/ohos" ohos:height="70vp" - ohos:width="match_parent" ohos:orientation="horizontal">
ohos:id="$+id:item_type" ohos:height="45vp" - ohos:width="50vp" ohos:image_src="$media:dv_phone"
- ohos:layout_alignment="vertical_center" ohos:left_margin="10vp"
- ohos:scale_mode="inside"/>
ohos:height="match_parent" ohos:width="0vp" - ohos:max_text_lines="1" ohos:text="Huawei P40"
- ohos:text_alignment="vertical_center" ohos:text_size="15fp"
- ohos:weight="1"/>
ohos:height="30vp" ohos:width="30vp" - ohos:image_src="$media:uncheck2"
ohos:height="340vp" ohos:width="match_parent" - ohos:orientation="vertical">
ohos:width="match_parent" ohos:background_element="$graphic:background_white_radius_10" - ohos:bottom_margin="50vp" ohos:left_margin="20vp"
- ohos:orientation="vertical" ohos:right_margin="20vp">
ohos:height="50vp" ohos:width="match_parent" - ohos:left_padding="20vp" ohos:text="迁移到其他设备"
- ohos:text_alignment="vertical_center" ohos:text_color="#000000"
- ohos:text_size="16fp"/>
ohos:height="0vp" ohos:width="match_parent" - ohos:weight="1"/>
ohos:width="match_parent" ohos:orientation="horizontal"> ohos:id="$+id:operate_no" ohos:height="match_parent" - ohos:width="0vp" ohos:text="取消"
- ohos:text_alignment="center" ohos:text_color="#1e90ff"
- ohos:text_size="17fp" ohos:weight="1"/>
ohos:height="25vp" ohos:width="1vp" - ohos:background_element="#cccccc" ohos:layout_alignment="vertical_center"/>
ohos:id="$+id:operate_yes" ohos:height="match_parent" - ohos:width="0vp" ohos:text="确定"
- ohos:text_alignment="center" ohos:text_color="#1e90ff"
- ohos:text_size="17fp" ohos:weight="1"/>
14. toast_dialog.xml
TV端
ohos:height="match_content" ohos:width="match_content" - ohos:orientation="vertical" ohos:background_element="$graphic:background_gray_circle">
ohos:height="match_content" ohos:width="match_content" - ohos:left_padding="16vp" ohos:right_padding="16vp"
- ohos:top_padding="4vp" ohos:bottom_padding="4vp"
- ohos:layout_alignment="center" ohos:text_size="20fp"/>
1. TVAbilitySlice
- 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
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
xmlns:ohos="http://schemas.huawei.com/res/ohos" ohos:height="match_parent" - ohos:width="match_parent" ohos:orientation="vertical"
- ohos:background_element="$media:haibao">
ohos:width="match_content" ohos:text="大棚数据监测" - ohos:text_color="#ffffff" ohos:text_size="26vp"
- ohos:layout_alignment="center" ohos:top_margin="50vp"/>
ohos:height="match_parent" ohos:width="match_parent"> ohos:height="match_content" ohos:width="match_parent" - ohos:left_padding="10vp" ohos:right_padding="10vp"
- ohos:center_in_parent="true" ohos:orientation="horizontal">
ohos:width="0" ohos:weight="1" - ohos:layout_alignment="center" ohos:orientation="vertical">
ohos:width="match_content" ohos:text="温度数据" - ohos:text_size="20vp" ohos:padding="5vp"
- ohos:text_color="#ffffff" ohos:layout_alignment="center"/>
ohos:width="match_parent" 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"/>
ohos:width="match_content" ohos:layout_alignment="center" - ohos:orientation="horizontal">
ohos:height="match_parent" ohos:width="match_content" - ohos:orientation="vertical">
ohos:height="0" ohos:width="match_content" - ohos:text="最高温度阀值:" ohos:text_size="16vp"
- ohos:weight="1" ohos:text_color="#000000"/>
ohos:width="match_content" ohos:text="当前温度:" - ohos:text_size="16vp" ohos:weight="1"
- ohos:text_color="#000000"/>
ohos:height="0" ohos:width="match_content" - ohos:text="最低温度阀值:" ohos:text_size="16vp"
- ohos:weight="1" ohos:text_color="#000000"/>
ohos:width="100vp"> ohos:id="$+id:rgb_tem" ohos:height="match_parent" - ohos:width="match_parent" ohos:progress_width="10vp"
- ohos:progress="0" ohos:max="100"
- ohos:start_angle="215" ohos:max_angle="290"
- ohos:progress_color="#00ff00" ohos:center_in_parent="true"/>
ohos:height="match_content" ohos:width="match_content" - ohos:text_size="15vp" ohos:text_color="#000000"
- ohos:align_parent_top="true" ohos:text="未知"
- ohos:top_margin="8vp" ohos:horizontal_center="true"/>
ohos:height="match_content" ohos:width="match_content" - ohos:text_size="15vp" ohos:text_color="#000000"
- ohos:text="未知" ohos:center_in_parent="true"/>
ohos:height="match_content" ohos:width="match_content" - ohos:text_size="14vp" ohos:text_color="#000000"
- ohos:text="未知" ohos:bottom_margin="8vp"
- ohos:align_parent_bottom="true" ohos:horizontal_center="true"/>
ohos:height="3vp" ohos:width="match_parent" - 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"/>
ohos:height="match_content" ohos:width="match_content" - ohos:layout_alignment="center" ohos:orientation="horizontal">
ohos:width="match_content" ohos:text="温度状态:" - ohos:text_size="18vp" ohos:text_color="#000000"/>
ohos:height="match_content" ohos:width="match_content" - ohos:text="未知" ohos:text_size="18vp"
- ohos:text_color="#000000"/>
ohos:height="90vp" ohos:width="1vp" - ohos:background_element="#000000" ohos:top_margin="6vp"
- ohos:layout_alignment="center"/>
ohos:width="0" ohos:weight="1" - ohos:layout_alignment="center" ohos:orientation="vertical">
ohos:width="match_content" ohos:text="湿度数据" - ohos:text_size="20vp" ohos:padding="5vp"
- ohos:text_color="#ffffff" ohos:layout_alignment="center"/>
ohos:width="match_parent" 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"/>
ohos:width="match_content" ohos:layout_alignment="center" - ohos:orientation="horizontal">
ohos:height="match_parent" ohos:width="match_content" - ohos:orientation="vertical">
ohos:height="0" ohos:width="match_content" - ohos:text="最大湿度阀值:" ohos:text_size="16vp"
- ohos:weight="1" ohos:text_color="#000000"/>
ohos:width="match_content" ohos:text="当前湿度:" - ohos:text_size="16vp" ohos:weight="1"
- ohos:text_color="#000000"/>
ohos:height="0" ohos:width="match_content" - ohos:text="最小湿度阀值:" ohos:text_size="16vp"
- ohos:weight="1" ohos:text_color="#000000"/>
ohos:width="100vp"> ohos:id="$+id:rgb_humi" ohos:height="match_parent" - ohos:width="match_parent" ohos:progress_width="10vp"
- ohos:progress="0" ohos:max="100"
- ohos:start_angle="215" ohos:max_angle="290"
- ohos:progress_color="#00ff00" ohos:center_in_parent="true"/>
ohos:height="match_content" ohos:width="match_content" - ohos:text_size="15vp" ohos:text_color="#000000"
- ohos:text="未知" ohos:top_margin="8vp"
- ohos:horizontal_center="true" ohos:align_parent_top="true"/>
ohos:height="match_content" ohos:width="match_content" - ohos:text_size="15vp" ohos:text_color="#000000"
- ohos:text="未知" ohos:center_in_parent="true"/>
ohos:height="match_content" ohos:width="match_content" - ohos:text_size="15vp" ohos:text_color="#000000"
- ohos:horizontal_center="true" ohos:text="未知"
- ohos:bottom_margin="8vp" ohos:align_parent_bottom="true"/>
ohos:width="match_parent" 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"/>
ohos:width="match_content" ohos:layout_alignment="center" - ohos:orientation="horizontal">
ohos:height="match_content" ohos:width="match_content" - ohos:text="湿度状态:" ohos:text_size="18vp"
- ohos:text_color="#000000"/>
ohos:id="$+id:tvHumiStatus" ohos:height="match_content" - ohos:width="match_content" ohos:text="未知"
- ohos:text_size="18vp" ohos:text_color="#000000"/>
ohos:width="1vp" ohos:background_element="#000000" - ohos:top_margin="6vp" ohos:layout_alignment="center"/>
ohos:width="0" ohos:weight="1" - ohos:layout_alignment="center" ohos:orientation="vertical">
ohos:width="match_content" ohos:text="CO2数据" - ohos:text_size="20vp" ohos:padding="5vp"
- ohos:text_color="#ffffff" ohos:layout_alignment="center"/>
ohos:width="match_parent" 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"/>
ohos:width="match_content" ohos:layout_alignment="center" - ohos:orientation="horizontal">
ohos:height="match_parent" ohos:width="match_content" - ohos:orientation="vertical">
ohos:height="0" ohos:width="match_content" - ohos:text="最大气体增量:" ohos:text_size="16vp"
- ohos:weight="1" ohos:text_color="#000000"/>
ohos:width="match_content" ohos:text="当前增量:" - ohos:text_size="16vp" ohos:weight="1"
- ohos:text_color="#000000"/>
ohos:height="0" ohos:width="match_content" - ohos:text="最小气体增量:" ohos:text_size="16vp"
- ohos:weight="1" ohos:text_color="#000000"/>
ohos:width="100vp"> ohos:id="$+id:rgb_gas" ohos:height="match_parent" - ohos:width="match_parent" ohos:progress_width="10vp"
- ohos:progress="0" ohos:max="1000"
- ohos:start_angle="215" ohos:max_angle="290"
- ohos:progress_color="#00ff00" ohos:center_in_parent="true"/>
ohos:height="match_content" ohos:width="match_content" - ohos:text_size="15vp" ohos:text="未知"
- ohos:text_color="#000000" ohos:top_margin="8vp"
- ohos:horizontal_center="true" ohos:align_parent_top="true"/>
ohos:height="match_content" ohos:width="match_content" - ohos:text_size="15vp" ohos:text="未知"
- ohos:text_color="#000000" ohos:center_in_parent="true"/>
ohos:height="match_content" ohos:width="match_content" - ohos:text_size="15vp" ohos:text="未知"
- ohos:text_color="#000000" ohos:bottom_margin="8vp"
- ohos:horizontal_center="true" ohos:align_parent_bottom="true"/>
ohos:height="3vp" ohos:width="match_parent" - 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"/>
ohos:height="match_content" ohos:width="match_content" - ohos:layout_alignment="center" ohos:orientation="horizontal">
ohos:width="match_content" ohos:text="气体状态:" - ohos:text_size="18vp" ohos:text_color="#000000"/>
ohos:height="match_content" ohos:width="match_content" - ohos:text="未知" ohos:text_size="18vp"
- ohos:text_color="#000000"/>
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": "" }
- ] }
- } }
想了解更多内容,请访问:
51CTO和华为官方合作共建的鸿蒙技术社区
https://harmonyos.51cto.co