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: numbertargetPkgName: stringappName: string}const TAG: string = 'RemoteDeviceModel'let SUBSCRIBE_ID: number = 100export 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 = undefinedregisterDeviceListCallback(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 = dmInstancethis.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 = callbackif (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)breakcase deviceManager.DeviceStateChange.UNKNOWN:this.changeStateOnline(data.device)breakcase deviceManager.DeviceStateChange.UNAVAILABLE:this.changeStateOffline(data.device)breakdefault: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] = dataLogger.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'@CustomDialogexport 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 = 0build() {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 = indexthis.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@Componentexport struct TitleBarComponent {@Prop isLand: boolean | null = null@State selectedIndex: number | undefined = 0@StorageLink('deviceList') deviceList: Array<deviceManager.DeviceBasicInfo> = []@Link isDistributed: booleanprivate isShow: boolean = falseprivate startAbilityCallBack: (key: string) => void = () => {}private dialogController: CustomDialogController | null = nullprivate remoteDeviceModel: RemoteDeviceModel = new RemoteDeviceModel()onSelectedIndexChange = async (index: number | undefined) => {Logger.info(TAG, `selectedIndexChange`)this.selectedIndex = indexif (this.selectedIndex === 0) {Logger.info(TAG, `stop ability`)await this.startAbilityCallBack(EXIT)this.isDistributed = falsethis.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 = trueif (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.UIAbilityContextlet 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.UIAbilityContextcontext.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 = undefinedpublic kvStore: distributedData.SingleKVStore | undefined = undefinedasync 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 = undefinedpublic kvStore: distributedData.SingleKVStore | undefined = undefinedasync 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@Componentstruct Index {@State isLand: boolean = false@State result: string = ''@State @Watch('dataChange') expression: string = ''@State isDistributed: boolean = false@State isShow: boolean = falseprivate 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按钮,表达式和运算结果归0this.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.UIAbilityContextlet 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 = truethis.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 万款的应用那么这么多的应用需要开发,也就意味着需要有更多的鸿蒙人才。鸿蒙开发工程师也将会迎来爆发式的增长,学习鸿蒙势在必行!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/809108.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ios包上架系列 一、打包机Flutter项目环境配置

打包的时候一定要断开网络&#xff0c;上线包名只能在打包机配置 1、Xcode 需要从其它电脑空投 版本号&#xff1a;14.3.1 升级到Xcode14.3后发现,从这个版本开始,苹果从Xcode中移除了ARC相关的库,会导致fluter项目下的原生工程使用Xcode编译原生代码没 有问题, 但是flutter项…

springboot上传模块到私服,再用pom引用下来

有时候要做一个公司的公共服务模块。不能说大家都直接把代码粘贴进去&#xff0c;因为会需要维护很多份&#xff1b;这样就剩下两个方式了。 方式一&#xff1a;自己独立部署一个公共服务的服务&#xff0c;全公司都调用&#xff0c;通过http、rpc或者grpc的方式&#xff0c;这…

如何安装PyFluent

0.什么是PyFluent? 官方介绍如下&#xff1a; PyFluent 是 PyAnsys 生态系统的一部分&#xff0c; 允许您在所选的 Python 环境中结合使用 Fluent 与其他 PyAnsys 库和外部 Python 库一起使用。 PyFluent 实现了客户端-服务器体系结构。它使用谷歌遥控器 过程调用或 gRPC 接…

基于GSP工具箱的NILM算法matlab仿真

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 5.完整工程文件 1.课题概述 基于GSP工具箱的NILM算法matlab仿真。GSP是图形信号处理的缩写&#xff0c;GSP非常适合对未知数据进行分类&#xff0c;尤其是当训练数据非常短时。GSPBox的基本理论是谱图论和…

构建强大的物联网平台系统架构:关键步骤与最佳实践

随着物联网&#xff08;IoT&#xff09;技术的快速发展和广泛应用&#xff0c;越来越多的企业开始意识到搭建一个强大而可靠的物联网平台系统架构的重要性。一个完善的物联网平台可以帮助企业高效地管理和监控各种连接设备&#xff0c;并实现数据的收集、处理和分析。在本文中&…

npm i -g nodemon 遇到的下载卡住及运行权限问题解决记录

一、下载nodemon原因 nodemon作用&#xff1a;用node环境运行js文件时可以实时刷新运行出结果 (即修改js代码后不需再手动重新运行js文件) 二、下载卡住 reify:semver:timing reifyNode:node_modules/nodemon Completed 卡住位置&#xff1a;reify:semver: timing reifyNode…

【muzzik 分享】3D模型平面切割

# 前言 一年一度的征稿到了&#xff0c;倒腾点存货&#xff0c;3D平面切割通常用于一些解压游戏里&#xff0c;例如水果忍者&#xff0c;切菜这些&#xff0c;今天我就给大家讲讲怎么实现3D切割以及其原理&#xff0c;帮助大家更理解3D中的 Mesh(网格)&#xff0c;以及UV贴图和…

2024年免费试用云服务器一览表

随着云计算技术的不断发展和普及&#xff0c;越来越多的企业和个人开始寻求通过云服务器来满足其数据存储、应用部署等需求。而免费试用云服务器则成为了一个吸引用户的重要手段&#xff0c;本文将为大家分享2024年免费试用云服务器一览表&#xff0c;帮助大家更好地选择合适的…

Spring Boot 框架集成Knife4j

本次示例使用 Spring Boot 作为脚手架来快速集成 Knife4j,Spring Boot 版本2.3.5.RELEASE,Knife4j 版本2.0.7&#xff0c;完整代码可以去参考 knife4j-spring-boot-fast-demo pom.xml 完整文件代码如下 <?xml version"1.0" encoding"UTF-8"?> &l…

https加载http不安全脚本提示解决方案

大家好&#xff0c;我是咕噜铁蛋。今天&#xff0c;我想和大家探讨一个很常见但又很容易被忽视的问题——https加载http不安全脚本提示。相信很多网站开发者和维护者在日常工作中都遇到过这样的问题&#xff0c;那么我们应该如何解决这个问题呢&#xff1f;下面&#xff0c;我将…

将Ubuntu18.04默认的python3.6升级到python3.8

1、查看现有的 python3 版本 python3 --version 2、安装 python3.8 sudo apt install python3.8 3、将 python3.6 和 3.8 添加到 update-alternatives sudo update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.6 1 sudo update-alternatives --insta…

算法设计与分析实验报告c++python实现(生命游戏、带锁的门、三壶谜题、串匹配问题、交替放置的碟子)

一、实验目的 1&#xff0e;加深学生对分治法算法设计方法的基本思想、基本步骤、基本方法的理解与掌握&#xff1b; 2&#xff0e;提高学生利用课堂所学知识解决实际问题的能力&#xff1b; 3&#xff0e;提高学生综合应用所学知识解决实际问题的能力。 二、实验任务 1、 编…

全面学习SpringCloud框架指南

要深入学习Spring Cloud框架,你需要系统地掌握其核心组件和概念,并了解如何在实际项目中应用这些知识。以下是一些关键的学习点和相应的学习内容: 一共分为10个模块包括: 1、微服务架构基础: 理解微服务架构的概念和优势。 学习单体架构向微服务架构演进的过程。 掌握…

计算机网络 Telnet远程访问交换机和Console终端连接交换机

一、实验要求和内容 1、配置交换机进入特权模式密文密码为“abcd两位班内学号”&#xff0c;远程登陆密码为“123456” 2、验证PC0通过远程登陆到交换机上&#xff0c;看是否可以进去特权模式 二、实验步骤 1、将一台还没配置的新交换机&#xff0c;利用console线连接设备的…

【高端电流检测IC储能产品应用方案】耐压28V侧轨的电流检测芯片FP130A 应用于电脑电源,开关电源以及多口快充充电器,户外移动电源,适配器,电池充电器等

电流检测技术常用于高压短路保护、电机控制、DC/DC换流器、系统功耗管理、二次电池的电流管理、蓄电池管理等电流侦测等场景。对于大多数应用而言&#xff0c;都是间接测量电阻两端的跨压差来获取待测电流。 如下面的高端电流检测芯片FP130A&#xff0c;丝印是FC915。电路原理图…

MySQL数据库的详解(1)

DDL&#xff08;数据库操作&#xff09; 查询 查询所有数据库&#xff1a;show databases;当前数据库&#xff1a;select database(); 创建 创建数据库&#xff1a;create database [ if not exists] 数据库名 ; 使用 使用数据库&#xff1a;use 数据库名 ; 删除 删除数…

利用Python实现可视化交互界面:Dash

Dash是一个低代码数据框架&#xff0c;用Python实现可视化交互界面&#xff0c;不用写Javascript&#xff0c;开源&#xff0c;支持回调、HTML组件等功能。 安装 pip install dash使用 # Import packages from dash import Dash, html, dash_table, dcc, callback, Output, …

网络安全JavaSE第六天

7. 数组 7.3.5 数组排序 7.3.5.1 冒泡排序 冒泡排序的思路&#xff1a;相邻两个元素进行比较&#xff0c;如果前面元素比后面元素大就交换位置&#xff0c;每一趟执行完后&#xff0c; 就会得到最大的数。 排序过程分析&#xff1a; package com.openlab; /** * 冒泡排序 */…

pyside6自定义部件库和软件框架的建设记录

一、自定义部件库 原则上尽量做到前后端分离&#xff0c;接口方便&#xff0c;复制简单。 1、单选框部件 # encoding: utf-8 ################################################### # 自定义的单选框 #################################################### 对外…

基于模型预测算法的含储能微网双层能量管理模型

基于模型预测算法的含储能微网双层能量管理模型 文章目录 基于模型预测算法的含储能微网双层能量管理模型一、项目介绍二、源程序下载 一、项目介绍 代码主要做的是一个微网双层优化调度模型&#xff0c;微网聚合单元包括风电、光伏、储能以及超级电容器&#xff0c;在微网的运…