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项…

Python小工具提升工作效率【附完整版,可下载word】

1、多个ppt汇总成一个ppt的python代码实现: import win32com.client as win32 import os import re def find(s): pat = re.compile(r"\d+") result = pat.match(s) return int(result.group(0)) def join_ppt(path:str): """ :param path: ppt所…

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的基本理论是谱图论和…

WPF网格布局

1介绍: Grid为WPF中最常用的布局容器, 它可以将一个区域分为若干个行或列功用其他组件使用。 2&#xff1a;网格标签Grid.ColumnDef Grid网格区域Grid.ColumnDefinitions自定义列 只能设置宽度 不能设置高度ColumnDefinition 网格布局中列的区域(可以设置宽度&#xff0c…

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

随着物联网&#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…

Flask、Django和Tornado怎么选

Flask、Django和Tornado是三个常用的PythonWeb框架&#xff0c;它们在设计理念、功能和适用场景上有所不同。下面是它们的对比&#xff1a; 1. 设计理念&#xff1a; - Flask是一个轻量级的框架&#xff0c;它提供了最基本的功能&#xff0c;但是具有高度的灵活性和可扩展性。…

Linux中的 mount -a

mount -a 是一个在 Linux 系统中用来挂载所有在 /etc/fstab 文件中定义的文件系统的命令。在 Linux 中&#xff0c;/etc/fstab 文件包含了系统启动时需要挂载的文件系统的信息&#xff0c;mount -a 命令会根据这些信息自动挂载这些文件系统。 mount -a 命令的作用&#xff1a;…

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…

Golang编译优化——消除Copy指令

一、优化概述 以下是Go编译器对某个代码段编译生成的SSA IR摘选&#xff0c;对于Golang SSA IR的介绍我写了文章&#xff0c;但是在犹豫要不要发。 b1:-... Plain → b2 (5)b2: ← b1 b4-v9 (5) Phi <int> v8 v16 (i[int])v22 (8) Phi <int> v7 v14 (r[int])v1…

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

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

减少服务器被入侵

减少服务器被入侵 排查方向 1、日志 查看/var/log下的日志&#xff0c;如果发现有大量SSH登录失败日志&#xff0c;并存在root用户多次登录失败后成功登录的记录&#xff0c;这就符合暴力P解特征 2、系统分析 对系统关键配置、账号、历史记录等进行排查&#xff0c;确认对系统…

全面学习SpringCloud框架指南

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

git常用命令整理~

在github创建仓库后的操作 git init git add . git commit -m 自定义 git remote add origin 仓库地址 git push -u origin master //第一次pushgit协作时用到的命令 git pull //push前先拉取别人的代码 git pushgit remote rm origin # 删除连接仓库git config --global use…