OpenHarmony开发案例:【分布式计算器】

 

介绍

本示例使用分布式能力实现了一个简单的计算器应用,可以进行简单的数值计算,支持远程拉起另一个设备的计算器应用,两个计算器应用进行协同计算。

远程拉起:通过StartAbility实现远端应用的拉起。

协同计算:通过DistributedDataKit分布式数据框架实现异端应用的数据同步。

本示例用到了媒体查询接口[@ohos.mediaquery]

分布式设备管理能力接口(设备管理),实现设备之间的kvStore对象的数据传输交互[@ohos.distributedHardware.deviceManager]

分布式数据管理接口[@ohos.data.distributedData]

效果预览

首页

使用说明

1.点击桌面应用图标,启动应用。

2.点击应用右上角按钮,或者在界面任意位置滑动(上下左右滑动皆可)即可弹出设备选择框。

3.在设备选择框中点击对端设备名称,拉起对端应用。

4.对端应用启动后,可在任意一端中操作应用,两端应用可实现数据实时同步。

5.在设备选择框中选中本机即可关闭对端应用。

相关概念

鸿蒙开发文档参考gitee.com/li-shizhen-skin/harmony-os/blob/master/README.md

数据管理实例: 用于获取KVStore的相关信息。

单版本分布式数据库:继承自KVStore,不对数据所属设备进行区分,提供查询数据和同步数据的方法。

具体实现

在分布式计算器应用中,分布式设备管理包含了分布式设备搜索、分布式设备列表弹窗、远端设备拉起三部分。
首先在分布式组网内搜索设备,然后把设备展示到分布式设备列表弹窗中,最后根据用户的选择拉起远端设备。

分布式设备搜索

搜狗高速浏览器截图20240326151450.png

通过SUBSCRIBE_ID搜索分布式组网内的远端设备,详见startDeviceDiscovery(){}模块[源码参考]。

/*鸿蒙开发知识已更新添加mau12379是v直接领取!

 * Copyright (c) 2022 Huawei Device Co., Ltd.

 * Licensed under the Apache License, Version 2.0 (the "License");

 * you may not use this file except in compliance with the License.

 * You may obtain a copy of the License at

 *

 *     http://www.apache.org/licenses/LICENSE-2.0

 *

 * Unless required by applicable law or agreed to in writing, software

 * distributed under the License is distributed on an "AS IS" BASIS,

 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

 * See the License for the specific language governing permissions and

 * limitations under the License.

 */



import deviceManager from '@ohos.distributedDeviceManager';

import Logger from '../model/Logger'

import { Callback } from '@ohos.base'



interface deviceData {

  device: deviceManager.DeviceBasicInfo

}



interface extraInfo {

  bindType: number

  targetPkgName: string

  appName: string

}



const TAG: string = 'RemoteDeviceModel'

let SUBSCRIBE_ID: number = 100



export const BUNDLE_NAME: string = 'ohos.samples.distributedcalc'



export class RemoteDeviceModel {

  public deviceList: Array<deviceManager.DeviceBasicInfo> | null = []

  public discoverList: Array<deviceManager.DeviceBasicInfo> = []

  private callback: () => void = () => {

  }

  private authCallback: () => void = () => {

  }

  private deviceManager: deviceManager.DeviceManager | undefined = undefined



  registerDeviceListCallback(callback: Callback<void>) {

    Logger.info(TAG, `deviceManager type =${typeof (this.deviceManager)} ,${JSON.stringify(this.deviceManager)} ,${JSON.stringify(this.deviceManager) === '{}'}`)

    if (typeof (this.deviceManager) !== 'undefined') {

      this.registerDeviceListCallbackImplement(callback)

      return

    }

    Logger.info(TAG, 'deviceManager.createDeviceManager begin')

    try {

      let dmInstance = deviceManager.createDeviceManager(BUNDLE_NAME);

      this.deviceManager = dmInstance

      this.registerDeviceListCallbackImplement(callback)

      Logger.info(TAG, `createDeviceManager callback returned, value= ${JSON.stringify(this.deviceManager)}`)

    } catch (error) {

      Logger.error(TAG, `createDeviceManager throw code:${error.code} message:${error.message}`)

    }

    Logger.info(TAG, 'deviceManager.createDeviceManager end')

  }



  changeStateOnline(device: deviceManager.DeviceBasicInfo) {

    if (this.deviceList !== null) {

      this.deviceList![this.deviceList!.length] = device;

    }

    Logger.debug(TAG, `online, device list= ${JSON.stringify(this.deviceList)}`);

    this.callback();

    if (this.authCallback !== null) {

      this.authCallback();

      this.authCallback = () => {

      }

    }

  }



  changeStateOffline(device: deviceManager.DeviceBasicInfo) {

    if (this.deviceList !== null && this.deviceList!.length > 0) {

      let list: Array<deviceManager.DeviceBasicInfo> = [];

      for (let j = 0; j < this.deviceList!.length; j++) {

        if (this.deviceList![j].deviceId !== device.deviceId) {

          list[j] = device;

        }

      }

      this.deviceList = list;

    }

    Logger.info(TAG, `offline, updated device list=${JSON.stringify(device)}`);

    this.callback();

  }



  changeState(device: deviceManager.DeviceBasicInfo, state: number) {

    if (this.deviceList !== null && this.deviceList!.length <= 0) {

      this.callback();

      return;

    }

    if (this.deviceList !== null && state === deviceManager.DeviceStateChange.AVAILABLE) {

      let list: Array<deviceManager.DeviceBasicInfo> = new Array();

      for (let i = 0; i < this.deviceList!.length; i++) {

        if (this.deviceList![i].deviceId !== device.deviceId) {

          list[i] = device;

        }

      }

      this.deviceList = list;

      Logger.debug(TAG, `ready, device list= ${JSON.stringify(device)}`);

      this.callback();

    } else {

      if (this.deviceList !== null) {

        for (let j = 0; j < this.deviceList!.length; j++) {

          if (this.deviceList![j].deviceId === device.deviceId) {

            this.deviceList![j] = device;

            break;

          }

        }

        Logger.debug(TAG, `offline, device list= ${JSON.stringify(this.deviceList)}`);

        this.callback();

      }

    }

  }



  registerDeviceListCallbackImplement(callback: Callback<void>) {

    Logger.info(TAG, 'registerDeviceListCallback')

    this.callback = callback

    if (this.deviceManager === undefined) {

      Logger.error(TAG, 'deviceManager has not initialized')

      this.callback()

      return

    }

    Logger.info(TAG, 'getTrustedDeviceListSync begin')

    try {

      let list = this.deviceManager !== undefined ? this.deviceManager.getAvailableDeviceListSync() : null;

      Logger.debug(TAG, `getTrustedDeviceListSync end, deviceList= ${JSON.stringify(list)}`);

      if (typeof (list) !== 'undefined' && JSON.stringify(list) !== '[]') {

        this.deviceList = list!;

      }

      Logger.info(TAG, `getTrustedDeviceListSync end, deviceList=${JSON.stringify(list)}`);

    } catch (error) {

      Logger.error(TAG, `getTrustedDeviceListSync throw code:${error.code} message:${error.message}`);

    }

    this.callback();

    Logger.info(TAG, 'callback finished');

    try {

      if (this.deviceManager !== undefined) {

        this.deviceManager.on('deviceStateChange', (data) => {

          if (data === null) {

            return

          }

          Logger.debug(TAG, `deviceStateChange data= ${JSON.stringify(data)}`)

          switch (data.action) {

            case deviceManager.DeviceStateChange.AVAILABLE:

              this.changeState(data.device, deviceManager.DeviceStateChange.AVAILABLE)

              break

            case deviceManager.DeviceStateChange.UNKNOWN:

              this.changeStateOnline(data.device)

              break

            case deviceManager.DeviceStateChange.UNAVAILABLE:

              this.changeStateOffline(data.device)

              break

            default:

              break

          }

        })

      }

      if (this.deviceManager !== undefined) {

        this.deviceManager.on('discoverSuccess', (data) => {

          if (data === null) {

            return

          }

          this.discoverList = []

          Logger.info(TAG, `discoverSuccess data=${JSON.stringify(data)}`)

          this.deviceFound(data.device)

        })

        this.deviceManager.on('discoverFailure', (data) => {

          Logger.info(TAG, `discoverFailure data= ${JSON.stringify(data)}`)

        })

        this.deviceManager.on('serviceDie', () => {

          Logger.error(TAG, 'serviceDie')

        })

      }

    } catch (error) {

      Logger.error(TAG, `on throw code:${error.code} message:${error.message}`)

    }

    this.startDeviceDiscovery()

  }



  deviceFound(data: deviceManager.DeviceBasicInfo) {

    for (let i = 0;i < this.discoverList.length; i++) {

      if (this.discoverList[i].deviceId === data.deviceId) {

        Logger.info(TAG, 'device founded ignored')

        return

      }

    }

    this.discoverList[this.discoverList.length] = data

    Logger.debug(TAG, `deviceFound self.discoverList= ${this.discoverList}`)

    this.callback()

  }



  /**

   * 通过SUBSCRIBE_ID搜索分布式组网内的设备

   */

  startDeviceDiscovery() {

    let discoverParam: Record<string, number> = {

      'discoverTargetType': 1

    };



    let filterOptions: Record<string, number> = {

      'availableStatus': 0,

    };



    Logger.info(TAG, `startDeviceDiscovery${SUBSCRIBE_ID}`);

    try {

      if (this.deviceManager !== undefined) {

        this.deviceManager.startDiscovering(discoverParam, filterOptions)

      }

    } catch (error) {

      Logger.error(TAG, `startDeviceDiscovery throw code:${error.code} message:${error.message}`)

    }

  }



  unregisterDeviceListCallback() {

    Logger.debug(TAG, `stopDeviceDiscovery ${SUBSCRIBE_ID}`)

    if (this.deviceManager === undefined) {

      return

    }

    if (this.deviceManager !== undefined) {

      try {

        Logger.info(TAG, `stopDiscovering`)

        this.deviceManager.stopDiscovering();

      } catch (error) {

        Logger.error(TAG, `stopDeviceDiscovery throw code:${JSON.stringify(error.code)} message:${error.message}`)

      }

      try {

        this.deviceManager.off('deviceStateChange')

        this.deviceManager.off('discoverSuccess')

        this.deviceManager.off('discoverFailure')

        this.deviceManager.off('serviceDie')

      } catch (error) {

        Logger.error(TAG, `off throw code:${error.code} message:${error.message}`)

      }

    }

    this.deviceList = []

    this.discoverList = []

  }



  authenticateDevice(device: deviceManager.DeviceBasicInfo, callBack: Callback<void>) {

    Logger.info(TAG, `authenticateDevice ${JSON.stringify(device)}`)

    for (let i = 0; i < this.discoverList.length; i++) {

      if (this.discoverList[i].deviceId !== device.deviceId) {

        continue

      }

      if (this.deviceManager === undefined) {

        return

      }

      try {

        if (this.deviceManager !== undefined) {

          this.deviceManager.bindTarget(device.deviceId, {

            bindType: 1,

            targetPkgName: BUNDLE_NAME,

            appName: 'Distributed distributecalc',

          }, (err, data) => {

            if (err) {

              Logger.error(TAG, `authenticateDevice error: ${JSON.stringify(err)}`)

              this.authCallback = () => {

              }

              return

            }

            Logger.debug(TAG, `authenticateDevice succeed: ${JSON.stringify(data)}`)

            this.authCallback = callBack

          })

        }

      } catch (error) {

        Logger.error(TAG, `authenticateDevice throw throw code:${error.code} message:${error.message}`)

      }

    }

  }

}
分布式设备列表弹窗

使用@CustomDialog装饰器来装饰分布式设备列表弹窗,[源码参考]。

/*

 * Copyright (c) 2022 Huawei Device Co., Ltd.

 * Licensed under the Apache License, Version 2.0 (the "License");

 * you may not use this file except in compliance with the License.

 * You may obtain a copy of the License at

 *

 *     http://www.apache.org/licenses/LICENSE-2.0

 *

 * Unless required by applicable law or agreed to in writing, software

 * distributed under the License is distributed on an "AS IS" BASIS,

 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

 * See the License for the specific language governing permissions and

 * limitations under the License.

 */

import deviceManager from '@ohos.distributedDeviceManager';

import Logger from '../model/Logger'



const TAG: string = 'DeviceDialog'



@CustomDialog

export struct DeviceDialog {

  controller?: CustomDialogController;

  @StorageLink('deviceList') deviceList: Array<deviceManager.DeviceBasicInfo> = AppStorage.get('deviceList')!;

  private selectedIndex: number | undefined = 0;

  private onSelectedIndexChange: (selectedIndex: number | undefined) => void = () => {

  }

  @State deviceDialogWidth: number = 0



  build() {

    Column() {

      Text($r('app.string.choiceDevice'))

        .fontSize(px2vp(30))

        .width('100%')

        .height('20%')

        .fontColor(Color.Black)

        .textAlign(TextAlign.Start)

      List() {

        ForEach(this.deviceList, (item: deviceManager.DeviceBasicInfo, index: number | undefined) => {

          ListItem() {

            Flex({ direction: FlexDirection.Row, justifyContent: FlexAlign.SpaceBetween }) {

              Text(item.deviceName)

                .fontSize(px2vp(30))

                .width('80%')

                .fontColor(Color.Black)

              Radio({ value: '', group: 'radioGroup' })

                .radioStyle({

                  checkedBackgroundColor: '#ff0d64fb'

                })

                .align(Alignment.Top)

                .width('3%')

                .checked(index === this.selectedIndex ? true : false)

            }

            .margin({ top: 17 })

            .onClick(() => {

              Logger.debug(TAG, `select device: ${item.deviceId}`)

              Logger.debug(TAG, `deviceList: ${JSON.stringify(this.deviceList)}`)

              if (this.selectedIndex !== undefined && index === this.selectedIndex) {

                Logger.info(TAG, `index:${JSON.stringify(index)} ty:${JSON.stringify(typeof (index))} this.selectedIndex:${JSON.stringify(this.selectedIndex)} ${JSON.stringify(typeof (this.selectedIndex))}`)

                return

              } else if (this.selectedIndex !== undefined) {

                this.selectedIndex = index

                this.onSelectedIndexChange(this.selectedIndex)

              }

            })

          }

          .width('100%')

          .height('40%')

        }, (item: deviceManager.DeviceBasicInfo) => item.deviceName)

      }

      .height('60%')

      .width('100%')

      .layoutWeight(1)



      Button() {

        Text($r('app.string.cancel'))

          .width('90%')

          .fontSize(21)

          .fontColor('#ff0d64fb')

          .textAlign(TextAlign.Center)

      }

      .type(ButtonType.Capsule)

      .backgroundColor(Color.White)

      .onClick(() => {

        if (this.controller !== undefined) {

          this.controller.close()

        }

      })

    }

    .margin({ bottom: 15 })

    .onAreaChange((oldArea: Area, newArea: Area) => {

      this.deviceDialogWidth = (newArea.width > newArea.height ? newArea.height : newArea.width) as number * 0.1 //percentage

    })

    .width('80%')

    .height(px2vp(240))

    .padding({ left: 18, right: 32 })

    .backgroundColor(Color.White)

    .border({ color: Color.White, radius: 20 })

  }

}
远端设备拉起

通过startAbility(deviceId)方法拉起远端设备的包,[源码参考]。

/*

 * Copyright (c) 2022 Huawei Device Co., Ltd.

 * Licensed under the Apache License, Version 2.0 (the "License");

 * you may not use this file except in compliance with the License.

 * You may obtain a copy of the License at

 *

 *     http://www.apache.org/licenses/LICENSE-2.0

 *

 * Unless required by applicable law or agreed to in writing, software

 * distributed under the License is distributed on an "AS IS" BASIS,

 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

 * See the License for the specific language governing permissions and

 * limitations under the License.

 */

import deviceManager from '@ohos.distributedDeviceManager';

import Logger from '../model/Logger'

import { DeviceDialog } from '../common/DeviceDialog'

import { RemoteDeviceModel, BUNDLE_NAME } from '../model/RemoteDeviceModel'

import common from '@ohos.app.ability.common'

import Want from '@ohos.app.ability.Want';



const TAG: string = 'TitleBar'

const DATA_CHANGE: string = 'dataChange'

const EXIT: string = 'exit'

const DEVICE_DISCOVERY_RANGE: number = 1000



@Component

export struct TitleBarComponent {

  @Prop isLand: boolean | null = null

  @State selectedIndex: number | undefined = 0

  @StorageLink('deviceList') deviceList: Array<deviceManager.DeviceBasicInfo> = []

  @Link isDistributed: boolean

  private isShow: boolean = false

  private startAbilityCallBack: (key: string) => void = () => {

  }

  private dialogController: CustomDialogController | null = null

  private remoteDeviceModel: RemoteDeviceModel = new RemoteDeviceModel()

  onSelectedIndexChange = async (index: number | undefined) => {

    Logger.info(TAG, `selectedIndexChange`)

    this.selectedIndex = index

    if (this.selectedIndex === 0) {

      Logger.info(TAG, `stop ability`)

      await this.startAbilityCallBack(EXIT)

      this.isDistributed = false

      this.deviceList = []

      if (this.dialogController !== null) {

        this.dialogController.close()

      }

      return

    }

    this.selectDevice()

  }



  aboutToAppear() {

    AppStorage.setOrCreate('deviceList', this.deviceList)

  }



  clearSelectState() {

    this.deviceList = []

    if (this.dialogController !== null) {

      this.dialogController.close()

    }

    Logger.info(TAG, `cancelDialog`)

    if (this.remoteDeviceModel === undefined) {

      return

    }

    this.remoteDeviceModel.unregisterDeviceListCallback()

  }



  selectDevice() {

    Logger.info(TAG, `start ability ......`)

    this.isDistributed = true

    if (this.selectedIndex !== undefined && (this.remoteDeviceModel === null || this.remoteDeviceModel.discoverList.length <= 0)) {

      Logger.info(TAG, `continue unauthed device: ${JSON.stringify(this.deviceList)}`)

      this.startAbility(this.deviceList[this.selectedIndex].networkId)

      this.clearSelectState()

      return

    }

    Logger.info(TAG, `start ability1, needAuth:`)

    if (this.selectedIndex !== undefined) {

      this.remoteDeviceModel.authenticateDevice(this.deviceList[this.selectedIndex], () => {

        Logger.info(TAG, `auth and online finished`);

        if (this.remoteDeviceModel !== null && this.remoteDeviceModel.deviceList !== null && this.selectedIndex !== undefined) {

          for (let i = 0; i < this.remoteDeviceModel.deviceList!.length; i++) {

            if (this.remoteDeviceModel.deviceList![i].deviceName === this.deviceList[this.selectedIndex].deviceName) {

              this.startAbility(this.remoteDeviceModel.deviceList![i].networkId);

            }

          }

        }

      })

    }

    Logger.info(TAG, `start ability2 ......`)

    this.clearSelectState()

  }



  async startAbility(deviceId: string | undefined) {

    Logger.debug(TAG, `startAbility deviceId: ${deviceId}`)

    let context = getContext(this) as common.UIAbilityContext

    let want: Want = {

      bundleName: BUNDLE_NAME,

      abilityName: 'MainAbility',

      deviceId: deviceId,

      parameters: {

        isRemote: 'isRemote'

      }

    }

    context.startAbility(want).then((data) => {

      Logger.info(TAG, `start ability finished: ${JSON.stringify(data)}`)

      this.startAbilityCallBack(DATA_CHANGE)

    })

  }



  showDiainfo() {

    this.deviceList = []

    // 注册监听回调,发现设备或查找到已认证设备会弹窗显示

    this.remoteDeviceModel.registerDeviceListCallback(() => {

      this.deviceList = []

      Logger.info(TAG, `registerDeviceListCallback, callback entered`)

      let context: common.UIAbilityContext | undefined = AppStorage.get('UIAbilityContext')

      if (context !== undefined) {

        this.deviceList.push({

          deviceId: '0',

          deviceName: context.resourceManager.getStringSync($r('app.string.localhost').id),

          deviceType: '0',

          networkId: ''

        })

      }

      let deviceTempList = this.remoteDeviceModel.discoverList.length > 0 ? this.remoteDeviceModel.discoverList : this.remoteDeviceModel.deviceList;

      if (deviceTempList !== null) {

        for (let i = 0; i < deviceTempList!.length; i++) {

          Logger.debug(TAG, `device ${i}/${deviceTempList!.length} deviceId= ${deviceTempList![i].deviceId},

        deviceName= ${deviceTempList![i].deviceName}, deviceType= ${deviceTempList![i].deviceType}`);

          if (deviceTempList !== null) {

            this.deviceList.push({

              deviceId: deviceTempList![i].deviceId,

              deviceName: deviceTempList![i].deviceName,

              deviceType: deviceTempList![i].deviceType,

              networkId: deviceTempList![i].networkId,

            })

            AppStorage.set('deviceList', this.deviceList)

          }

        }

      }

    })

    if (this.dialogController === null) {

      this.dialogController = new CustomDialogController({

        builder: DeviceDialog({

          selectedIndex: this.selectedIndex,

          onSelectedIndexChange: this.onSelectedIndexChange

        }),

        cancel: () => {

          this.clearSelectState()

        },

        autoCancel: true,

        alignment: this.isLand ? DialogAlignment.Center : DialogAlignment.Bottom,

        customStyle: false

      })

    }

    if (this.dialogController !== null) {

      this.dialogController.open()

    }

  }



  build() {

    Row() {

      Image($r('app.media.ic_back'))

        .height('60%')

        .margin({ left: '5%' })

        .width('50px')

        .objectFit(ImageFit.Contain)

        .onClick(async () => {

          let context = getContext(this) as common.UIAbilityContext

          context.terminateSelf()

        })

      Text($r('app.string.distributed_calculator'))

        .height('60%')

        .fontSize('28px')

        .margin({ left: 12 })

      Blank().layoutWeight(1)

      if (!this.isShow) {

        Image($r("app.media.ic_hop_normal1"))

          .id('selectDevice')

          .margin({ right: 32 })

          .width('9%')

          .margin({ right: '12%' })

          .objectFit(ImageFit.Contain)

          .onClick(() => {

            this.showDiainfo()

          })

      }

    }

    .width('100%')

    .height(this.isLand ? '10%' : '6%')

    .constraintSize({ minHeight: 50 })

    .alignItems(VerticalAlign.Center)

  }

}
分布式数据管理

(1) 管理分布式数据库
创建一个KVManager对象实例,用于管理分布式数据库对象。通过distributedData.createKVManager(config),并通过指定Options和storeId,创建并获取KVStore数据库,并通过Promise方式返回,此方法为异步方法,例如this.kvManager.getKVStore(STORE_ID, options).then((store) => {}),[源码参考]。

/*

 * Copyright (c) 2022 Huawei Device Co., Ltd.

 * Licensed under the Apache License, Version 2.0 (the "License");

 * you may not use this file except in compliance with the License.

 * You may obtain a copy of the License at

 *

 *     http://www.apache.org/licenses/LICENSE-2.0

 *

 * Unless required by applicable law or agreed to in writing, software

 * distributed under the License is distributed on an "AS IS" BASIS,

 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

 * See the License for the specific language governing permissions and

 * limitations under the License.

 */

import distributedData from '@ohos.data.distributedKVStore';

import Logger from '../model/Logger'

import { BUNDLE_NAME } from './RemoteDeviceModel'

import common from '@ohos.app.ability.common';

import { Callback } from '@ohos.base';



const TAG: string = 'KvStoreModel'

const STORE_ID: string = 'distributedcalc'



export class KvStoreModel {

  public kvManager: distributedData.KVManager | undefined = undefined

  public kvStore: distributedData.SingleKVStore | undefined = undefined



  async createKvStore(context: common.BaseContext, callback: Callback<void>) {

    if ((typeof (this.kvStore) !== 'undefined')) {

      callback()

      return

    }

    let config: distributedData.KVManagerConfig = {

      bundleName: BUNDLE_NAME,

      context: context

    };

    try {

      Logger.info(TAG, `ecreateKVManager success`);

      this.kvManager = distributedData.createKVManager(config);

    } catch (err) {

      Logger.info(TAG, `ecreateKVManager err:${JSON.stringify(err)}`);

    }

    Logger.info(TAG, `createKVManager begin`);

    let options: distributedData.Options = {

      createIfMissing: true,

      encrypt: false,

      backup: false,

      autoSync: true,

      kvStoreType: distributedData.KVStoreType.DEVICE_COLLABORATION,

      securityLevel: distributedData.SecurityLevel.S1

    };

    Logger.info(TAG, `kvManager.getKVStore begin`);

    if (this.kvManager !== undefined) {

      this.kvManager.getKVStore(STORE_ID, options, (err, store: distributedData.SingleKVStore) => {

        Logger.info(TAG, `getKVStore success, kvStore= ${store}`);

        this.kvStore = store;

        callback();

      })

    }

    Logger.info(TAG, `createKVManager end`)

  }



  deleteKvStore() {

    if (this.kvStore !== undefined && this.kvStore !== null) {

      return;

    }

    try {

      if (this.kvManager !== undefined) {

        Logger.info(TAG, 'deleteKvStore success')

        this.kvManager.deleteKVStore(BUNDLE_NAME, STORE_ID)

      }

    } catch (err) {

      Logger.error(TAG, 'deleteKvStore error error is:' + JSON.stringify(err))

    }

  }



  put(key: string, value: string) {

    if (this.kvStore) {

      Logger.debug(TAG, `kvStore.put ${key} = ${value}`)

      this.kvStore.put(

        key,

        value

      ).then((data) => {

        Logger.debug(TAG, `kvStore.put ${key} finished, data= ${JSON.stringify(data)}`)

      }).catch((err: object) => {

        Logger.debug(TAG, `kvStore.put ${key} failed, ${JSON.stringify(err)}`)

      })

    }

  }



  setOnMessageReceivedListener(context: common.UIAbilityContext, msg: string, callback: Callback<string>) {

    Logger.info(TAG, `setOnMessageReceivedListener: ${msg}`);

    this.createKvStore(context, () => {

      Logger.info(TAG, `kvStore.on(dataChange) begin`);

      if (this.kvStore !== undefined && this.kvStore !== null) {

        try {

          this.kvStore!.on('dataChange', distributedData.SubscribeType.SUBSCRIBE_TYPE_REMOTE, (data) => {

            Logger.debug(TAG, `dataChange, ${JSON.stringify(data)}`);

            let entries = data.insertEntries.length > 0 ? data.insertEntries : data.updateEntries;

            for (let i = 0; i < entries.length; i++) {

              if (entries[i].key === msg) {

                let value = entries[i].value.value.toString();

                Logger.debug(TAG, `Entries receive msg :${msg}, value:${value}`);

                callback(value);

                return;

              }

            }

          })

        } catch (err) {

          Logger.error(TAG, `kvStore.on(dataChange) err :` + err);

        }

      }

      Logger.info(TAG, `kvStore.on(dataChange) end`);

    })

  }

}

(2) 订阅分布式数据变化
通过订阅分布式数据库所有(本地及远端)数据变化实现数据协同,[源码参考]。

/*

 * Copyright (c) 2022 Huawei Device Co., Ltd.

 * Licensed under the Apache License, Version 2.0 (the "License");

 * you may not use this file except in compliance with the License.

 * You may obtain a copy of the License at

 *

 *     http://www.apache.org/licenses/LICENSE-2.0

 *

 * Unless required by applicable law or agreed to in writing, software

 * distributed under the License is distributed on an "AS IS" BASIS,

 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

 * See the License for the specific language governing permissions and

 * limitations under the License.

 */

import distributedData from '@ohos.data.distributedKVStore';

import Logger from '../model/Logger'

import { BUNDLE_NAME } from './RemoteDeviceModel'

import common from '@ohos.app.ability.common';

import { Callback } from '@ohos.base';



const TAG: string = 'KvStoreModel'

const STORE_ID: string = 'distributedcalc'



export class KvStoreModel {

  public kvManager: distributedData.KVManager | undefined = undefined

  public kvStore: distributedData.SingleKVStore | undefined = undefined



  async createKvStore(context: common.BaseContext, callback: Callback<void>) {

    if ((typeof (this.kvStore) !== 'undefined')) {

      callback()

      return

    }

    let config: distributedData.KVManagerConfig = {

      bundleName: BUNDLE_NAME,

      context: context

    };

    try {

      Logger.info(TAG, `ecreateKVManager success`);

      this.kvManager = distributedData.createKVManager(config);

    } catch (err) {

      Logger.info(TAG, `ecreateKVManager err:${JSON.stringify(err)}`);

    }

    Logger.info(TAG, `createKVManager begin`);

    let options: distributedData.Options = {

      createIfMissing: true,

      encrypt: false,

      backup: false,

      autoSync: true,

      kvStoreType: distributedData.KVStoreType.DEVICE_COLLABORATION,

      securityLevel: distributedData.SecurityLevel.S1

    };

    Logger.info(TAG, `kvManager.getKVStore begin`);

    if (this.kvManager !== undefined) {

      this.kvManager.getKVStore(STORE_ID, options, (err, store: distributedData.SingleKVStore) => {

        Logger.info(TAG, `getKVStore success, kvStore= ${store}`);

        this.kvStore = store;

        callback();

      })

    }

    Logger.info(TAG, `createKVManager end`)

  }



  deleteKvStore() {

    if (this.kvStore !== undefined && this.kvStore !== null) {

      return;

    }

    try {

      if (this.kvManager !== undefined) {

        Logger.info(TAG, 'deleteKvStore success')

        this.kvManager.deleteKVStore(BUNDLE_NAME, STORE_ID)

      }

    } catch (err) {

      Logger.error(TAG, 'deleteKvStore error error is:' + JSON.stringify(err))

    }

  }



  put(key: string, value: string) {

    if (this.kvStore) {

      Logger.debug(TAG, `kvStore.put ${key} = ${value}`)

      this.kvStore.put(

        key,

        value

      ).then((data) => {

        Logger.debug(TAG, `kvStore.put ${key} finished, data= ${JSON.stringify(data)}`)

      }).catch((err: object) => {

        Logger.debug(TAG, `kvStore.put ${key} failed, ${JSON.stringify(err)}`)

      })

    }

  }



  setOnMessageReceivedListener(context: common.UIAbilityContext, msg: string, callback: Callback<string>) {

    Logger.info(TAG, `setOnMessageReceivedListener: ${msg}`);

    this.createKvStore(context, () => {

      Logger.info(TAG, `kvStore.on(dataChange) begin`);

      if (this.kvStore !== undefined && this.kvStore !== null) {

        try {

          this.kvStore!.on('dataChange', distributedData.SubscribeType.SUBSCRIBE_TYPE_REMOTE, (data) => {

            Logger.debug(TAG, `dataChange, ${JSON.stringify(data)}`);

            let entries = data.insertEntries.length > 0 ? data.insertEntries : data.updateEntries;

            for (let i = 0; i < entries.length; i++) {

              if (entries[i].key === msg) {

                let value = entries[i].value.value.toString();

                Logger.debug(TAG, `Entries receive msg :${msg}, value:${value}`);

                callback(value);

                return;

              }

            }

          })

        } catch (err) {

          Logger.error(TAG, `kvStore.on(dataChange) err :` + err);

        }

      }

      Logger.info(TAG, `kvStore.on(dataChange) end`);

    })

  }

}
计算器模块

1、监听变化:通过this.listener.on('change', this.onLand)监听当前设备按钮状态,当改变时通过getContext(this).requestPermissionsFromUser(['ohos.permission.DISTRIBUTED_DATASYNC'])获取不同设备间的数据交换权限。
2、判断设备状态:当AppStorage.Get('isRemote')==='isRemote'时,将isDistributed状态置为true。 3、订阅分布式数据变化: 通过kvStoreModel.setOnMessageReceivedListener(DATA_CHANGE, (value) => {},其中根据isDistributed的值决定如何操作分布式计算器:为true时且输入的值不是EXIT状态把值放进expression中进行数据计算,当输入的值为空时,将expression的值置空。
4、特殊功能按钮:

  • 当用户点击C按钮,表达式和运算结果归0。 将this.expression = ''; this.result = '';[源码参考]。
/*

 * Copyright (c) 2022 Huawei Device Co., Ltd.

 * Licensed under the Apache License, Version 2.0 (the "License");

 * you may not use this file except in compliance with the License.

 * You may obtain a copy of the License at

 *

 *     http://www.apache.org/licenses/LICENSE-2.0

 *

 * Unless required by applicable law or agreed to in writing, software

 * distributed under the License is distributed on an "AS IS" BASIS,

 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

 * See the License for the specific language governing permissions and

 * limitations under the License.

 */

import mediaQuery from '@ohos.mediaquery'

import Logger from '../model/Logger'

import { ButtonComponent } from '../common/ButtonComponent'

import { ButtonComponentHorizontal } from '../common/ButtonComponentHorizontal'

import { InputComponent } from '../common/InputComponent'

import { KvStoreModel } from '../model/KvStoreModel'

import { RemoteDeviceModel } from '../model/RemoteDeviceModel'

import { TitleBarComponent } from '../common/TitleBarComponent'

import { isOperator, calc } from '../model/Calculator'

import abilityAccessCtrl from '@ohos.abilityAccessCtrl'

import common from '@ohos.app.ability.common';

import mediaquery from '@ohos.mediaquery';



const TAG: string = 'Index'

const EXIT: string = 'exit'

const DATA_CHANGE: string = 'dataChange'



@Entry

@Component

struct Index {

  @State isLand: boolean = false

  @State result: string = ''

  @State @Watch('dataChange') expression: string = ''

  @State isDistributed: boolean = false

  @State isShow: boolean = false

  private listener = mediaQuery.matchMediaSync('screen and (min-aspect-ratio: 1.5) or (orientation: landscape)')

  private kvStoreModel: KvStoreModel = new KvStoreModel()

  private remoteDeviceModel: RemoteDeviceModel = new RemoteDeviceModel()

  onLand = (mediaQueryResult: mediaquery.MediaQueryResult) => {

    Logger.debug(TAG, `onLand: mediaQueryResult.matches= ${mediaQueryResult.matches}`)

    if (mediaQueryResult.matches) {

      this.isLand = true

    } else {

      this.isLand = false

    }

  }



  dataChange() {

    Logger.info(TAG, `dataChange, expression = ${this.expression}`)

    this.kvStoreModel.put(DATA_CHANGE, this.expression)

  }



  isOperator(operator: string) {

    return (

      operator === '+' || operator === '-' || operator === '*' || operator === '/'

    )

  }



  onInputValue = (value: string) => {

    Logger.info(TAG, `this.isLand=${this.isLand}`);

    if (value === 'C') { // 当用户点击C按钮,表达式和运算结果归0

      this.expression = '';

      this.result = '';

      return;

    } else if (value === 'D') {

      this.expression = this.expression.substring(0, this.expression.length - 1);

      this.result = this.result = calc(this.expression);

      if (!this.expression.length) {

        this.result = '';

        Logger.info(TAG, `handleBackspace`);

      }

    } else if (isOperator(value)) {

      Logger.info(TAG, `value=${value}`);

      let size = this.expression.length;

      if (size) {

        const last = this.expression.charAt(size - 1);

        if (isOperator(last)) {

          this.expression = this.expression.substring(0, this.expression.length - 1);

        }

      }

      if (!this.expression && (value === '*' || value === '/')) {

        return;

      }

      this.expression += value;

    } else if (value === '=') {

      this.result = calc(this.expression);

      if (this.result !== '' && this.result !== undefined) {

        this.expression = this.result;

        this.result = '';

      }

    } else {

      this.expression += value;

      this.result = calc(this.expression);

    }

  }



  aboutToDisappear() {

    Logger.info(TAG, `index disappear`)

    this.kvStoreModel.deleteKvStore()

  }



  async aboutToAppear() {

    this.listener.on('change', this.onLand)

    let context = getContext(this) as common.UIAbilityContext

    let atManager = abilityAccessCtrl.createAtManager()

    try {

      atManager.requestPermissionsFromUser(context, ['ohos.permission.DISTRIBUTED_DATASYNC']).then((data) => {

        Logger.info(TAG, `data: ${JSON.stringify(data)}`)

      }).catch((err: object) => {

        Logger.info(TAG, `err: ${JSON.stringify(err)}`)

      })

    } catch (err) {

      Logger.info(TAG, `catch err->${JSON.stringify(err)}`)

    }

    Logger.info(TAG, `grantPermission,requestPermissionsFromUser`)

    let isRemote: string | undefined = AppStorage.get('isRemote')

    if (isRemote === 'isRemote' ? true : false) {

      this.isDistributed = true

      this.isShow = true

    }

    this.kvStoreModel.setOnMessageReceivedListener(context, DATA_CHANGE, (value: string) => {

      Logger.debug(TAG, `DATA_CHANGE: ${value},this.isDistributed = ${this.isDistributed}`)

      if (this.isDistributed) {

        if (value.search(EXIT) !== -1) {

          Logger.info(TAG, `EXIT ${EXIT}`)

          context.terminateSelf((error) => {

            Logger.error(TAG, `terminateSelf finished, error= ${error}`)

          })

        } else {

          if (value === 'null') {

            this.expression = ''

          } else {

            this.expression = value

          }

          if (this.isOperator(this.expression.substr(this.expression.length - 1, this.expression.length))) {

            this.result = calc(this.expression.substring(0, this.expression.length - 1))

          } else {

            this.result = calc(this.expression)

          }

        }

      }

    })

  }



  startAbilityCallBack = (key: string) => {

    Logger.info(TAG, `startAbilityCallBack ${key}`)

    if (DATA_CHANGE === key) {

      this.kvStoreModel.put(DATA_CHANGE, this.expression)

    }

    if (EXIT === key) {

      this.kvStoreModel.put(DATA_CHANGE, EXIT)

    }

  }



  build() {

    Column() {

      TitleBarComponent({

        isLand: this.isLand,

        startAbilityCallBack: this.startAbilityCallBack,

        remoteDeviceModel: this.remoteDeviceModel,

        isDistributed: $isDistributed,

        isShow: this.isShow

      })

      if (this.isLand) {

        Row() {

          InputComponent({ isLand: this.isLand, result: $result, expression: $expression })

          ButtonComponentHorizontal({ onInputValue: this.onInputValue })

        }

        .width('100%')

        .layoutWeight(1)

      } else {

        Column() {

          InputComponent({ isLand: this.isLand, result: $result, expression: $expression })

          ButtonComponent({ onInputValue: this.onInputValue })

        }

        .width('100%')

      }

    }

    .width('100%')

    .height('100%')

  }

}
  • 当用户点击“X”按钮后,删除运算表达式的最后一个字符。
  • 当用户点击“=”按钮后,将调用calc(this.expression)对表达式进行数据计算。

最后呢,很多开发朋友不知道需要学习那些鸿蒙技术?鸿蒙开发岗位需要掌握那些核心技术点?为此鸿蒙的开发学习必须要系统性的进行。

而网上有关鸿蒙的开发资料非常的少,假如你想学好鸿蒙的应用开发与系统底层开发。你可以参考这份资料,少走很多弯路,节省没必要的麻烦。由两位前阿里高级研发工程师联合打造《鸿蒙NEXT星河版OpenHarmony开发文档》里面内容包含了(ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、音频、视频、WebGL、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、Harmony南向开发、鸿蒙项目实战等等)鸿蒙(Harmony NEXT)技术知识点

如果你是一名Android、Java、前端等等开发人员,想要转入鸿蒙方向发展。可以直接领取这份资料辅助你的学习。下面是鸿蒙开发的学习路线图。

高清完整版请点击→《鸿蒙NEXT星河版开发学习文档》

针对鸿蒙成长路线打造的鸿蒙学习文档。话不多说,我们直接看详细资料鸿蒙(OpenHarmony )学习手册(共计1236页)与鸿蒙(OpenHarmony )开发入门教学视频,帮助大家在技术的道路上更进一步。

《鸿蒙 (OpenHarmony)开发学习视频》

图片

《鸿蒙生态应用开发V2.0白皮书》

图片

《鸿蒙 (OpenHarmony)开发基础到实战手册》

获取这份鸿蒙星河版学习资料,请点击→《鸿蒙NEXT星河版开发学习文档》

OpenHarmony北向、南向开发环境搭建

图片

《鸿蒙开发基础》

  1. ArkTS语言

  2. 安装DevEco Studio

  3. 运用你的第一个ArkTS应用

  4. ArkUI声明式UI开发

  5. .……

图片

《鸿蒙开发进阶》

  1. Stage模型入门

  2. 网络管理

  3. 数据管理

  4. 电话服务

  5. 分布式应用开发

  6. 通知与窗口管理

  7. 多媒体技术

  8. 安全技能

  9. 任务管理

  10. WebGL

  11. 国际化开发

  12. 应用测试

  13. DFX面向未来设计

  14. 鸿蒙系统移植和裁剪定制

  15. ……

图片

《鸿蒙开发实战》

  1. ArkTS实践

  2. UIAbility应用

  3. 网络案例

  4. ……

图片

 获取这份鸿蒙星河版学习资料,请点击→《鸿蒙NEXT星河版开发学习文档》

总结

鸿蒙—作为国家主力推送的国产操作系统。部分的高校已经取消了安卓课程,从而开设鸿蒙课程;企业纷纷跟进启动了鸿蒙研发

并且鸿蒙是完全具备无与伦比的机遇和潜力的;预计到年底将有 5,000 款的应用完成原生鸿蒙开发,未来将会支持 50 万款的应用那么这么多的应用需要开发,也就意味着需要有更多的鸿蒙人才。鸿蒙开发工程师也将会迎来爆发式的增长,学习鸿蒙势在必行!

  • 14
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值