HarmonyOS鸿蒙实战( Beta6版)分布式数据对象的跨设备数据同步实践

160 篇文章 0 订阅
160 篇文章 0 订阅

场景

传统跨设备数据同步,需开发者完成:建立通信链接、消息收发处理、错误重试、数据冲突解决等一系列消息处理逻辑,工作量大且复杂。

设备之间的数据都是"变量",分布式数据对象即实现了对“变量”的“全局”访问。为开发者在分布式应用场景下提供简单易用的JS接口,轻松实现多设备间同应用的数据协同,同时设备间可以监听对象的状态和数据变更。

方案介绍

使用分布式数据对象进行跨设备数据同步时,需要申请ohos.permission.DISTRIBUTED_DATASYNC权限,允许不同设备间的数据交换,该权限为user_grant,需要向用户弹窗申请授权。

随后创建分布式数据对象并加入可信组网(进行跨设备数据同步的设备需要登录相同的华为账号,同一网络,并开启蓝牙)。

注:一个分布式对象加入组网时,如果它的数据与组网中的数据不同,它会刷新组网中的数据。若希望加入组网时不刷新组网中的数据,并且得到组网中的数据,可以将属性设置为undefined。(例如下文中的Message对象设置为:new Message(undefined,undefined) )。

实现效果

A、B两台设备分别点击创建分布式数据对象,并开启数据变更监听,此时在A设备输入数据,B设备即可自动同步。

开发步骤

一、申请权限

使用分布式数据对象进行跨设备数据同步时,需要申请ohos.permission.DISTRIBUTED_DATASYNC权限,该权限为user_grant,需要向用户弹窗申请授权。用户若拒绝授权,需要引导用户到设置--隐私和安全--多设备协同中授权,调用分布式数据对象相关能力时,建议先判断用户是否已授权。申请权限可参考:

//EntryAbility.ets
function reqPermissionsFromUser(permissions: Array<Permissions>,context:common.UIAbilityContext): void {
  let atManager = abilityAccessCtrl.createAtManager();
  // requestPermissionsFromUser会判断权限的授权状态来决定是否唤起弹窗
  atManager.requestPermissionsFromUser(context, permissions).then((data) => {
    let grantStatus: Array<number> = data.authResults;
    let length: number = grantStatus.length;
    for (let i = 0; i < length; i++) {
      if (grantStatus[i] === 0) {
        // 用户授权,可以继续访问目标操作
        console.info(`User authorization succeeded.`)
      }
      else {
        // 用户拒绝授权,提示用户必须授权才能访问当前页面的功能,并引导用户到系统设置中打开相应的权限
        console.error("user did not grant!")
        openPermissionsInSystemSettings(context);
      }
    }
    // 授权成功
  }).catch((err: String) => {
  })
}

function openPermissionsInSystemSettings(context: common.UIAbilityContext): void {
  let wantInfo: Want = {
    bundleName: 'com.huawei.hmos.settings',
    abilityName: 'com.huawei.hmos.settings.MainAbility',
    uri: 'application_info_entry',
    parameters: {
      pushParams: 'com.example.distributeddata' // 打开指定应用的详情页面
    }
  }
  context.startAbility(wantInfo).then(() => {
    // ...
  }).catch((err: BusinessError) => {
    // ...
  })
}
​
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
  const permissions: Array<Permissions> = ['ohos.permission.DISTRIBUTED_DATASYNC'];
  reqPermissionsFromUser(permissions,this.context)
  hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate');
}

二、创建分布式对象并加入组网

实现方案:

封装DistributedDataModel 类,创建实例时根据传入的source:Object对象(本文传入一个Message对象,包含createTime,message两个属性),通过distributedDataObject.create方法创建对应的分布式数据对象并通过setSessionId方法设置其sessionId。

封装单例类GlobalThis用于存储mesaage,并通过Observed装饰,当message变化时刷新到TextArea中。

注:同一个设备只能有一个分布式对象加入相同的sessionId,A对象已经加入sessionId后,B对象在加入同一sessionId会失败。

核心代码:

import distributedDataObject from '@ohos.data.distributedDataObject';
import common from '@ohos.app.ability.common';
import { BusinessError } from '@kit.BasicServicesKit';
import { ShowData } from '../utils/ShowData'

let localObject: distributedDataObject.DataObject;

export default class DistributedDataModel {
  showData: ShowData = ShowData.getInstance()

  constructor(context: common.Context, source: Object) {
    if (localObject == null || undefined) {
      localObject = distributedDataObject.create(context, source);
      localObject.setSessionId('123456')
      console.info('setSessionId end');
    } else {
      console.log('请勿重复创建分布式数据对象并加入同一sessionID')
    }
  }

  //设置message
  setMessage(message: string) {
    if (localObject != null || undefined) {
      localObject['message'] = message;
    } else {
      console.log('localObject is null or undefined')
    }
  }
}
// 构造单例对象
@Observed
export class ShowData {
  private static instance: ShowData;
  public message: string;

  constructor(message: string) {
    this.message = message
  }

  public static getInstance(): ShowData {
    if (!ShowData.instance) {
      ShowData.instance = new ShowData('');
    }
    return ShowData.instance;
  }

  getMessage(): string {
    return this.message;
  }

  setMessage(value: string): void {
    this.message = value;
  }
}
import DistributedDataModel from '../DistributedData/DistributedDataModel'
import { ShowData } from '../utils/ShowData'

class Message {
  createTime?: Date
  message?: string
  constructor(createTime?: Date, message?: string) {
    this.createTime = createTime
    this.message = message
  }
}

let now = Date.now()
let distributedDataA: DistributedDataModel;

@Entry
@Component
struct Index {
  @State messages: Message = new Message(new Date(now), '这是初始数据');
  @State showData: ShowData = ShowData.getInstance()
  controller: TextAreaController = new TextAreaController()

  build() {
    Row() {
      Column() {
        TextArea({
          text: this.showData.message,
          placeholder: 'The text area can hold an unlimited amount of text. input your word...',
          controller: this.controller
        })
          .placeholderFont({ size: 16, weight: 400 })
          .width(336)
          .height(56)
          .margin(20)
          .fontSize(16)
          .fontColor('#182431')
          .backgroundColor('#FFFFFF')
          .onChange((value: string) => {

            distributedDataA.setMessage(value)
          })
        Button('创建分布式数据对象')
          .fontSize(20)
          .fontWeight(FontWeight.Bold)
          .onClick(() => {
            distributedDataA = new DistributedDataModel(getContext(), this.messages);
            //如果设备加入组网时不希望刷新组网中已有的数据,注意将该分布式数据对象的属性设置为undefined,即messages的属性设置为undefined
            // distributedDataA= new DistributedDataModel(getContext(),new Message(undefined,undefined));
          })
          .margin(10)
      }
      .height('100%')
      .width('100%')
    }
  }
}

三、操作分布式数据对象

(1)监听数据变更。

A、B两条设备组网后,A设备开启监听后(on("change")),B设备修改数据,随后A设备读取数据,此时读取到的是B设备修改后的数据。

随后A删除监听(off("change")),B设备再次修改数据,随后A设备读取数据,此时读取到的是B设备修改前的数据。

import { ShowData } from '../utils/ShowData'

showData: ShowData = ShowData.getInstance()

//获取message
getMessage(): string {
  if (localObject != null || undefined) {
    console.log('获取组网内最新数据 createTime is ' + localObject['createTime'])
    console.log('获取组网内最新数据  message is ' + localObject['message'])
    return localObject['message']
  } else {
    console.log('localObject is null or undefined')
    return ''
  }
}

//设置message
setMessage(message: string) {
  if (localObject != null || undefined) {
    localObject['message'] = message;
  } else {
    console.log('localObject is null or undefined')
  }
}

//监听对象数据变更。可监听对端数据的变更,以callback作为变更回调实例。
onChange() {
  localObject.on('change', (sessionId: string, fields: Array<string>) => {
    console.info('change' + sessionId);
    if (fields != null && fields != undefined) {
      for (let index: number = 0; index < fields.length; index++) {
        this.showData.setMessage(localObject[fields[index]])
        console.info(`message is ${this.showData.getMessage()}`)
        console.info(`The element ${localObject[fields[index]]} changed.`);
      }
    }
  });
}

// 删除数据变更监听 当不再进行数据变更监听时,使用此接口删除对象的变更监听
deleteChange() {
  if (localObject != null || undefined) {
    // 删除数据变更回调changeCallback
    localObject.off('change', (sessionId: string, fields: Array<string>) => {
      console.info('change' + sessionId);
      if (fields != null && fields != undefined) {
        for (let index: number = 0; index < fields.length; index++) {
          console.info('changed !' + fields[index] + ' ' + localObject[fields[index]]);
        }
      }
    });
    // 删除所有的数据变更回调
    localObject.off('change');
  } else {
    console.log('localObject is null or undefined')
  }
}

(2)监听组网内分布式数据对象的上下线及移除监听。

通过on("status")监听分布式对象的上下线,当对端设备上下线时即可触发业务回调,当不在需要监听时可通过 off("status")移除监听。

//监听分布式设备上下线
onStatus() {
  if (localObject != null || undefined) {
    localObject.on('status', (sessionId: string, networkId: string, status: 'online' | 'offline') => {
      console.info('status changed'  + sessionId + ' '+ status + ' ' + networkId);
    });
  } else {
    console.log('localObject is null or undefined')
  }
}

//  删除设备上下线监听 当不再进行对象上下线监听时,使用此接口删除对象的上下线监听。
deleteStatus() {
  if (localObject != null || undefined) {
    // 删除上下线回调changeCallback
    /*localObject.off('status', (sessionId: string, networkId: string, status: 'online' | 'offline') => {
      console.info('status changed ' + sessionId + ' ' + status + ' ' + networkId);
    });*/
    // 删除所有的上下线回调
    localObject.off('status');
  } else {
    console.log('localObject is null or undefined')
  }
}

(3)保存分布式数据对象及撤回保存的分布式数据对象。

save(deviceId: string),保存分布式数据对象。deviceId表示保存数据的设备号,标识需要保存对象的设备,默认为"local",代表保存在本地。

通过save("local")将对象数据保存成功后,当应用存在时不会释放对象数据,当应用退出后,重新进入应用时,恢复保存在设备上的数据。

通过revokeSave()撤回保存的分布式数据对象,如果对象保存在本地设备,那么将删除所有受信任设备上所保存的数据。如果对象保存在其他设备,那么将删除本地设备上的数据。

注意:revokesave是撤销还没有被恢复的数据。若A设备将数据save到B设备,B设备创建分布式对象时会自动恢复数据,此时A设备revokesave无法撤销B设备已回复的数据。

deviceId获取方式 :

1、on('status')的时候可以获得标识对象设备的networkId(使用该networkId即可)。

2、 在接续场景中,可以重wantParam里获取targetDevice,参考链接:应用接续

// 保存分布式数据对象
saveData() {
  if (localObject != null || undefined) {
    // 保存数据对象,如果应用退出后组网内设备需要恢复对象数据时调用
    localObject.save('local').then((result: distributedDataObject.SaveSuccessResponse) => {
      console.info(`Succeeded in saving. SessionId:${result.sessionId},
        version:${result.version},deviceId:${result.deviceId}`);
    }).catch((err: BusinessError) => {
      console.error(`Failed to save. Code:${err.code},message:${err.message}`);
    });
  } else {
    console.log('localObject is null or undefined')
  }
}

// 撤回保存的分布式数据对象
revokeSaveData() {
  if (localObject != null || undefined) {
    // 撤回保存的数据对象
    localObject.revokeSave().then((result: distributedDataObject.RevokeSaveSuccessResponse) => {
      console.info(`Succeeded in revokeSaving. Session:${result.sessionId}`);
    }).catch((err: BusinessError) => {
      console.error(`Failed to revokeSave. Code:${err.code},message:${err.message}`);
    });
  } else {
    console.log('localObject is null or undefined')
  }
}

最后

小编在之前的鸿蒙系统扫盲中,有很多朋友给我留言,不同的角度的问了一些问题,我明显感觉到一点,那就是许多人参与鸿蒙开发,但是又不知道从哪里下手,因为资料太多,太杂,教授的人也多,无从选择。有很多小伙伴不知道学习哪些鸿蒙开发技术?不知道需要重点掌握哪些鸿蒙应用开发知识点?而且学习时频繁踩坑,最终浪费大量时间。所以有一份实用的鸿蒙(HarmonyOS NEXT)文档用来跟着学习是非常有必要的。 

为了确保高效学习,建议规划清晰的学习路线,涵盖以下关键阶段:

 →【纯血版鸿蒙全套最新学习文档】希望这一份鸿蒙学习文档能够给大家带来帮助~


 鸿蒙(HarmonyOS NEXT)最新学习路线

该路线图包含基础技能、就业必备技能、多媒体技术、六大电商APP、进阶高级技能、实战就业级设备开发,不仅补充了华为官网未涉及的解决方案

路线图适合人群:

IT开发人员:想要拓展职业边界
零基础小白:鸿蒙爱好者,希望从0到1学习,增加一项技能。
技术提升/进阶跳槽:发展瓶颈期,提升职场竞争力,快速掌握鸿蒙技术

2.学习视频+学习PDF文档

HarmonyOS Next 最新全套视频教程 (鸿蒙语法ArkTS、TypeScript、ArkUI教程……)

​​

 纯血版鸿蒙全套学习文档(面试、文档、全套视频等)

                   

​​​​鸿蒙APP开发必备

​​

总结

【纯血版鸿蒙全套最新学习文档】

总的来说,华为鸿蒙不再兼容安卓,对程序员来说是一个挑战,也是一个机会。只有积极应对变化,不断学习和提升自己,才能在这个变革的时代中立于不败之地。 

  • 9
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值