介绍
本示例使用分布式能力实现了一个简单的计算器应用,可以进行简单的数值计算,支持远程拉起另一个设备的计算器应用,两个计算器应用进行协同计算。
远程拉起:通过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,不对数据所属设备进行区分,提供查询数据和同步数据的方法。
具体实现
在分布式计算器应用中,分布式设备管理包含了分布式设备搜索、分布式设备列表弹窗、远端设备拉起三部分。
首先在分布式组网内搜索设备,然后把设备展示到分布式设备列表弹窗中,最后根据用户的选择拉起远端设备。
分布式设备搜索
通过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北向、南向开发环境搭建
《鸿蒙开发基础》
-
ArkTS语言
-
安装DevEco Studio
-
运用你的第一个ArkTS应用
-
ArkUI声明式UI开发
-
.……
《鸿蒙开发进阶》
-
Stage模型入门
-
网络管理
-
数据管理
-
电话服务
-
分布式应用开发
-
通知与窗口管理
-
多媒体技术
-
安全技能
-
任务管理
-
WebGL
-
国际化开发
-
应用测试
-
DFX面向未来设计
-
鸿蒙系统移植和裁剪定制
-
……
《鸿蒙开发实战》
-
ArkTS实践
-
UIAbility应用
-
网络案例
-
……
获取这份鸿蒙星河版学习资料,请点击→《鸿蒙NEXT星河版开发学习文档》
总结
鸿蒙—作为国家主力推送的国产操作系统。部分的高校已经取消了安卓课程,从而开设鸿蒙课程;企业纷纷跟进启动了鸿蒙研发。
并且鸿蒙是完全具备无与伦比的机遇和潜力的;预计到年底将有 5,000 款的应用完成原生鸿蒙开发,未来将会支持 50 万款的应用。那么这么多的应用需要开发,也就意味着需要有更多的鸿蒙人才。鸿蒙开发工程师也将会迎来爆发式的增长,学习鸿蒙势在必行!