鸿蒙HarmonyOS开发实例:【分布式关系型数据库】

 

介绍

本示例使用[@ohos.data.relationalStore]接口和[@ohos.distributedDeviceManager] 接口展示了在eTS中分布式关系型数据库的使用,在增、删、改、查的基本操作外,还包括分布式数据库的数据同步同能。

效果预览

image.png

使用说明:

  1. 启动应用后点击“ + ”按钮可以添加联系人;
  2. 点击联系人可以进入编辑界面编辑联系人信息;
  3. 长按联系人进入多选状态,底部有“全选”、“取消”、“删除”、“退出”按钮,点击退出可以退出多选状态;
  4. 点击右上角更多按钮,点击“连接设备”,选择要同步数据的设备,连接成功后可以开始将本端数据同步到对端;
  5. 点击右上角更多按钮,点击“设置”可以进入设置界面设置数据同步方式,包括自动同步和手动同步。 

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

具体实现

  • 数据库的增、删、改、查操作都在RdbModel中,源码参考[RdbModel.ets]:
/** Copyright (c) 2023 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 data_rdb from '@ohos.data.relationalStore'import common from '@ohos.app.ability.common'import Contact from '../model/Contact'import Logger from '../model/Logger'import { STORE_CONFIG } from '../model/RdbConst'import { ValuesBucket } from '@ohos.data.ValuesBucket';const TAG = 'RdbModel'export default class RdbModel {private rdbStore: data_rdb.RdbStore | undefined = undefinedprivate tableName: string = ''private sqlCreateTable: string = ''private columns: Array<string> = []private distributedTable: string = ''private dataChangeCallback : Function| null = nullprivate isCreateDbDone: boolean = falseprivate context: common.UIAbilityContextconstructor(tableName: string, sqlCreateTable: string, columns: Array<string>, context: common.UIAbilityContext) {this.tableName = tableNamethis.sqlCreateTable = sqlCreateTablethis.columns = columnsthis.context = contextthis.getRdbStore()}// 初始化数据库async getRdbStore() {Logger.info(TAG, 'getRdbStore begin')if (this.isCreateDbDone) {Logger.info(TAG, 'getRdbStore isCreateDbDone')return}try {// 获取数据库存储对象this.rdbStore = await data_rdb.getRdbStore(this.context, STORE_CONFIG);} catch (err) {console.info(`getRdbStore err ${JSON.stringify(err)}`);}Logger.info(TAG, 'getRdbStore end')try {// 执行sql语句,联系人个各个属性设定if(this.rdbStore != undefined) {await this.rdbStore.executeSql(this.sqlCreateTable)console.info(`create tabe start ` + this.sqlCreateTable);// 设置分布式表,表明为contactawait this.rdbStore.setDistributedTables([this.tableName])}} catch (e) {Logger.error(TAG, 'getRdbStore:' + JSON.stringify(e))}// 分布式数据库创建为完成this.isCreateDbDone = trueLogger.info(TAG, 'create table done')}async insertData(contact: Contact) {let value1 = contact.name;let value2 = contact.gender;let value3 = contact.phone;let value4 = contact.remark;let value5 = contact.age;const valueBucket: ValuesBucket = {'name': value1,'gender': value2,'phone': value3,'remark': value4,'age': value5,}if(this.rdbStore != undefined) {let ret = await this.rdbStore.insert(this.tableName, valueBucket, data_rdb.ConflictResolution.ON_CONFLICT_REPLACE)Logger.info(TAG, `insert done:${ret}`)}}async updateData(contact: Contact) {let value1 = contact.name;let value2 = contact.gender;let value3 = contact.phone;let value4 = contact.remark;let value5 = contact.age;const valueBucket: ValuesBucket = {'name': value1,'gender': value2,'phone': value3,'remark': value4,'age': value5,}let predicates = new data_rdb.RdbPredicates(this.tableName)Logger.info(TAG, `updateData id=${contact.id}`)predicates.equalTo('id', contact.id)if (this.rdbStore != undefined) {let ret = await this.rdbStore.update(valueBucket, predicates)Logger.info(TAG, `updated row count: ${ret}`)}}async deleteContacts(contacts: Array<Contact>) {let predicates = new data_rdb.RdbPredicates(this.tableName)contacts.forEach((contact) => {predicates.or().equalTo('id', contact.id)})if (this.rdbStore != undefined) {let rows = await this.rdbStore.delete(predicates)Logger.info(TAG, `delete rows: ${rows}`)}}async query(predicates: data_rdb.RdbPredicates): Promise<Array<Contact>> {Logger.info(TAG, 'query start')Logger.info(TAG, 'predicates is ' + JSON.stringify(predicates))Logger.info(TAG, 'columns ' + JSON.stringify(this.columns))if (this.rdbStore != undefined) {// 默认查询所有列let resultSet: data_rdb.ResultSet = await this.rdbStore.query(predicates, this.columns);Logger.info(TAG, 'result is ' + JSON.stringify(resultSet.rowCount))// 处理查询到的结果数组return this.getListFromResultSet(resultSet)}return []}async syncData(predicates: data_rdb.RdbPredicates) {Logger.info(TAG, 'syncData')if (this.rdbStore != undefined) {let result = await this.rdbStore.sync(data_rdb.SyncMode.SYNC_MODE_PUSH, predicates)for (let i = 0; i < result.length; i++) {Logger.info(TAG, `device=${result[i][0]}, status = ${result[i][1]}`)}}}async onDataChange(device: string, callback: Function) {Logger.info(TAG, `onDataChange enter,device=` + device + ` ,tableName = ` + this.tableName)try {if (this.rdbStore != undefined) {this.distributedTable = await this.rdbStore.obtainDistributedTableName(device, this.tableName)Logger.info(TAG, `obtainDistributedTableName,distributedTable=` + this.distributedTable)}}catch (err) {Logger.error(TAG, `ObtainDistributedTableName failed, code is ${err.code},message is ${err.message}`)}this.dataChangeCallback = callbackawait this.pullData()if (this.rdbStore != undefined) {this.rdbStore.on('dataChange', data_rdb.SubscribeType.SUBSCRIBE_TYPE_REMOTE, async (devices) => {Logger.info(TAG, `on dataChange, callback`)await this.pullData()})}}async pullData() {Logger.info(TAG, `start pullData`)if (this.rdbStore != undefined) {await this.rdbStore.executeSql('delete from ' + this.tableName)let predicates = new data_rdb.RdbPredicates(this.distributedTable)let resultSet = await this.rdbStore.query(predicates, this.columns)let result = this.getListFromResultSet(resultSet)Logger.info(TAG, `on dataChange,result.length=${result.length}`)for (let i = 0; i < result.length; i++) {Logger.info(TAG, `on dataChange,insert${result[i].name}`)let predicate = new data_rdb.RdbPredicates(this.tableName)predicate.equalTo('name', result[i].name)let exit = await this.rdbStore.query(predicate, this.columns)exit.goToFirstRow()if (exit.rowCount === 0) {await this.insertData(result[i])} else {result[i].id = exit.getDouble(resultSet.getColumnIndex('id'))await this.updateData(result[i])}}if (this.dataChangeCallback != null) {this.dataChangeCallback(result)}}}offDataChange() {if(this.rdbStore != undefined) {this.rdbStore.off('dataChange', data_rdb.SubscribeType.SUBSCRIBE_TYPE_REMOTE, (devices) => {for (let i = 0; i < devices.length; i++) {Logger.info(TAG, `device=${devices[i]} off data changed`)}})}}// 处理数据格式getListFromResultSet(resultSet: data_rdb.ResultSet): Array<Contact> {// 声明结果变量let contacts: Array<Contact> = []// 进入结果集的第一行resultSet.goToFirstRow()// 如果没有结束就继续遍历while (!resultSet.isEnded) {// 读取各个属性,初始化临时变量contactlet contact: Contact = new Contact(resultSet.getDouble(resultSet.getColumnIndex('id')), resultSet.getString(resultSet.getColumnIndex('name')), resultSet.getDouble(resultSet.getColumnIndex('gender')), resultSet.getString(resultSet.getColumnIndex('phone')), resultSet.getLong(resultSet.getColumnIndex('age')), resultSet.getString(resultSet.getColumnIndex('remark')))if (!contacts.includes(contact)) {// 如果数据集合中没有这条数据就添加进去contacts.push(contact)}// 进入下一行resultSet.goToNextRow()}// 数据整合完毕就释放资源resultSet.close()Logger.info(TAG, 'contacts number is ' + contacts.length)// 返回整合的联系人数据return contacts}}
  • 数据库操作:使用[@ohos.data.relationalStore] 接口的getRdbStore获得一个相关的操作型关系数据库RdbStore,通过这个RdbStore调用相关接口进行增删改查,RdbStore.insert数据插入,RdbStore.delete数据删除,RdbStore.update更新数据,RdbStore.query根据条件查询数据;

  • 数据同步:RdbStore.on注册数据库观察者,使用RdbStore.obtainDistributedTableName根据本地表名获取指定远程设备的分布式表名,数据发生变动时通过RdbStore.sync同步数据,不需要用时删除指定观察者使用RdbStore.off。

  • 连接设备管理在RemoteDeviceModel中,源码参考[RemoteDeviceModel.ets]:

/** Copyright (c) 2023 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 { BUNDLE } from '../model/RdbConst'import { BusinessError } from '@ohos.base';let SUBSCRIBE_ID = 100const TAG: string = 'RemoteDeviceModel'class Data {device: deviceManager.DeviceBasicInfo = {deviceId: "",deviceName: "",deviceType: "",networkId: "",}}class RemoteDeviceModel {public deviceList: Array<deviceManager.DeviceBasicInfo> = [];public discoverList: Array<deviceManager.DeviceBasicInfo> = [];private callback: () => void = () => {};private authCallback: (device: deviceManager.DeviceBasicInfo) => void = (device: deviceManager.DeviceBasicInfo) => '';private deviceManager: deviceManager.DeviceManager | undefined = undefinedregisterDeviceListCallback(callback: () => void) {if (typeof (this.deviceManager) !== 'undefined') {this.registerDeviceListCallbackImplement(callback)return}Logger.info(TAG, 'deviceManager.createDeviceManager begin')try {let dmInstance = deviceManager.createDeviceManager(BUNDLE);Logger.info(TAG, `dmInstance= ${JSON.stringify (dmInstance)}`);this.deviceManager = dmInstance;this.registerDeviceListCallbackImplement(callback);Logger.info(TAG, `createDeviceManager callback returned, value= ${JSON.stringify(this.deviceManager)}`);} catch (error) {Logger.error(TAG, `createDeviceManager throw error, code: ${(error as BusinessError).code} message: ${(error as BusinessError).message}`);}Logger.info(TAG, 'deviceManager.createDeviceManager end');}deviceStateChangeActionOnline(device: deviceManager.DeviceBasicInfo) {this.deviceList[this.deviceList.length] = deviceLogger.info(TAG, `online, device list=${JSON.stringify(this.deviceList)}`)if (this.authCallback !== undefined) {this.authCallback(device)this.authCallback = () => {}}}deviceStateChangeActionReady(device: deviceManager.DeviceBasicInfo) {if (this.deviceList.length <= 0) {this.callback()return}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 = listLogger.info(TAG, `ready, device list=${JSON.stringify(this.deviceList)}`)this.callback()}deviceStateChangeActionOffline(device: deviceManager.DeviceBasicInfo) {if (this.deviceList.length <= 0) {this.callback()return}for (let j = 0; j < this.deviceList.length; j++) {if (this.deviceList[j].deviceId === device.deviceId) {this.deviceList[j] = devicebreak}}Logger.info(TAG, `offline, device list=${JSON.stringify(this.deviceList)}`)}getLocalDevice(): string {Logger.info(TAG, `getLocalDevice`);if(this.deviceManager != undefined) {let deviceId: string = this.deviceManager.getLocalDeviceId();Logger.info(TAG, `local deviceInfo=${JSON.stringify(deviceId)}`);return deviceId;}return ''}registerDeviceListCallbackImplement(callback: () => void) {Logger.info(TAG, 'registerDeviceListCallback' + JSON.stringify(this.deviceManager))this.callback = callbackif (this.deviceManager === undefined) {Logger.error(TAG, 'deviceManager has not initialized')this.callback()return}Logger.info(TAG, 'getTrustedDeviceListSync begin' + JSON.stringify(this.deviceManager));let list = this.deviceManager.getAvailableDeviceListSync();Logger.info(TAG, `getTrustedDeviceListSync end, deviceList=${JSON.stringify(list)}`)if (typeof (list) !== 'undefined' && typeof (list.length) !== 'undefined') {this.deviceList = list}this.callback()Logger.info(TAG, 'callback finished')this.deviceManager.on('deviceStateChange', (data) => {Logger.info(TAG, 'deviceStateChange on:' + JSON.stringify(data));if (data === null) {return}switch (data.action) {case deviceManager.DeviceStateChange.UNKNOWN:this.deviceStateChangeActionOnline(data.device)breakcase deviceManager.DeviceStateChange.AVAILABLE:this.deviceStateChangeActionReady(data.device)breakcase deviceManager.DeviceStateChange.UNAVAILABLE:this.deviceStateChangeActionOffline(data.device)breakdefault:break}})this.deviceManager.on('discoverSuccess', (data: Data) => {if (data === null) {return}Logger.info(TAG, `discoverSuccess data=${JSON.stringify(data)}`);this.deviceFound(data);})this.deviceManager.on('discoverFailure', (data) => {Logger.info(TAG, `discoverFailure data=${JSON.stringify(data)}`);})this.deviceManager.on('serviceDie', () => {Logger.info(TAG, 'serviceDie')})this.startDeviceDiscovery()}deviceFound(data: Data) {if(data != undefined) {if (data.device != undefined) {for (let i = 0; i < this.discoverList.length; i++) {if (this.discoverList[i].deviceId === data.device.deviceId) {Logger.info(TAG, 'device founded ignored')return}}this.discoverList[this.discoverList.length] = data.deviceLogger.info(TAG, `deviceFound self.discoverList=${JSON.stringify(this.discoverList)}`);this.callback()}}}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 error, code: ${(error as BusinessError).code} message: ${(error as BusinessError).message}`);}}unregisterDeviceListCallback() {if(this.deviceManager != undefined) {Logger.info(TAG, `stopDeviceDiscovery${SUBSCRIBE_ID}`);this.deviceManager.stopDiscovering();this.deviceManager.off('deviceStateChange');this.deviceManager.off('discoverSuccess');this.deviceManager.off('discoverFailure');this.deviceManager.off('serviceDie');}this.deviceList = [];this.discoverList = [];}authenticateDevice(device: deviceManager.DeviceBasicInfo, callBack: (device: deviceManager.DeviceBasicInfo) => void) {Logger.info(TAG, `bindTarget${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 !== null) {this.deviceManager.bindTarget(device.deviceId, {bindType: 1,targetPkgName: BUNDLE,appName: 'Distributed distributedrdb',}, (err, data) => {if (err) {Logger.error(TAG, `authenticateDevice throw error, code: ${(err as BusinessError).code} message: ${(err as BusinessError).message}`);this.authCallback = () => {}return}Logger.debug(TAG, `authenticateDevice succeed: ${JSON.stringify(data)}`);this.authCallback = callBack;})}} catch (error) {Logger.error(TAG, `authenticateDevice throw error, code: ${(error as BusinessError).code} message: ${(error as BusinessError).message}`);}}}}export default new RemoteDeviceModel()
  • 设备同步:设备同步数据需要[ohos.permission.DISTRIBUTED_DATASYNC] 权限,在页面渲染前申请权限,源码参考[Index.ets],
/** Copyright (c) 2022-2023 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 data_rdb from '@ohos.data.relationalStore'import common from '@ohos.app.ability.common'import Want from '@ohos.app.ability.Want'import router from '@ohos.router'import Contact from '../model/Contact'import ContactDataSource from '../common/BasicDataSource'import LiteStore from '../model/LiteStore'import Logger from '../model/Logger'import RdbModel from '../model/RdbModel'import { BottomBtn } from '../common/BottomBtn'import { ContactItem } from '../common/ContactItem'import { PopupMenu } from '../common/PopupMenu'import { SyncState } from '../model/LiteStore'import { SearchBar } from '../common/SearchBar'import { TitleBar } from '../common/TitleBar'import { TABLE_NAME, BUNDLE, ABILITY, SQL_CREATE_TABLE, COLUMNS } from '../model/RdbConst'const TAG: string = 'Index'export interface stateType {popupMenu: boolean,isDistributed: boolean,isStage: boolean,selectedIndex: number,syncState: string,distributedDevice: string,opacityValue: number}@Entry@Componentstruct Index {private liteStore = new LiteStore("sync_state", getContext(this) as common.UIAbilityContext)private rdbModel = new RdbModel(TABLE_NAME, SQL_CREATE_TABLE, COLUMNS, getContext(this) as common.UIAbilityContext)private intervalId: number = 0@State contacts: ContactDataSource = new ContactDataSource([])@State isMultiCheck: boolean = false@State isSelectedAll: boolean = false@State state: stateType = {popupMenu: false,isDistributed: false,isStage: false,selectedIndex: 0,syncState: SyncState.ManualSync,distributedDevice: '',opacityValue: 1}async aboutToAppear() {Logger.info(TAG, 'aboutToAppear')await this.rdbModel.getRdbStore();await this.getData();}// 拉起应用后读取数据,暂定为分布式功能getWant() {let want = AppStorage.Get<Want>('want') as Wantif(want.parameters != undefined) {if (this.state.isDistributed && want.parameters.isStage === 'EXIT') {Logger.info(TAG, 'isStage = EXIT')this.state.isStage = falsethis.state.isDistributed = falsethis.state.selectedIndex = 0this.state.distributedDevice = ''this.rdbModel.offDataChange()}if (!this.state.isDistributed && want.parameters.isStage === 'Stage') {Logger.info(TAG, 'isStage = Stage')this.state.isStage = truethis.state.isDistributed = truethis.state.distributedDevice = want.parameters.dmsSrcNetworkId as string;let context = getContext(this) as common.UIAbilityContextcontext.startAbility({bundleName: BUNDLE,abilityName: ABILITY,deviceId: this.state.distributedDevice,parameters: {isStage: 'CONNECT'}})Logger.info(TAG, 'onDataChange')this.rdbModel.onDataChange(this.state.distributedDevice, (result: Array<Contact>)=> {this.contacts.dataArray = resultthis.contacts.notifyDataReload()})}}}async onPageShow() {try {// 初始化分部署数据库await this.rdbModel.getRdbStore()this.intervalId = setInterval(() => {// 手动侦听应用被拉起的动作this.getWant()}, 1000)// 读取数据库数据await this.getData()} catch (err) {Logger.error('onPageShow:' + JSON.stringify(err))}}async getData() {Logger.info(TAG, 'getData')// 初始化数据库的表,表名为contactlet predicates = new data_rdb.RdbPredicates(TABLE_NAME)// 读取表中的数据this.contacts.replaceDataArray(await this.rdbModel.query(predicates));// 通知懒加载数据变更this.contacts.notifyDataReload()Logger.info(TAG, 'getData contacts count' + this.contacts.dataArray.length)// 读取Preferences中的数据let syncState = await this.liteStore.getValue()this.state.syncState = `${syncState}`if (!this.state.isStage && this.state.isDistributed && syncState === SyncState.AutomaticSync) {await this.syncData()}}showDeleteDialog() {let deleteContacts: Contact[] = []this.contacts.dataArray.forEach((contact) => {if (contact.isSelected) {deleteContacts.push(contact)}})if (deleteContacts.length == 0) {return}AlertDialog.show({message: $r('app.string.delete_contact'),primaryButton: {value: $r('app.string.sure'),fontColor: Color.Red,action: async () => {await this.rdbModel.deleteContacts(deleteContacts)await this.getData()this.quitMultiCheck()}},secondaryButton: {value: $r('app.string.cancel'),fontColor: Color.Blue,action: () => {}}})}handleClickContact(item: Contact, index: number) {Logger.info(TAG, `handleClickContact, item = ${JSON.stringify(item)}`)if (this.isMultiCheck) {let tempContacts = this.contacts.dataArraythis.contacts.dataArray = []tempContacts[index].isSelected = !item.isSelectedthis.contacts.dataArray = tempContactsthis.contacts.notifyDataReload()} else {router.pushUrl({url: 'pages/ContactEdit',params: { contact: item, isInsert: false }})}}refreshSelectState(isSelect: boolean) {this.contacts.dataArray.forEach((contact) => {contact.isSelected = isSelect})this.contacts.notifyDataReload()}quitMultiCheck() {this.isSelectedAll = falsethis.refreshSelectState(this.isSelectedAll)this.isMultiCheck = false}handleBottomBtnClick = (index: number) => {switch (index) {case 0:this.isSelectedAll = !this.isSelectedAllthis.refreshSelectState(this.isSelectedAll)breakcase 1:this.showDeleteDialog()breakcase 2:this.quitMultiCheck()breakdefault:break}}handleRightBtn = () => {this.state.popupMenu = true;this.state.opacityValue = 1;}syncData = () => {Logger.info(TAG, 'sync data')let predicates = new data_rdb.RdbPredicates(TABLE_NAME)predicates.inAllDevices()this.rdbModel.syncData(predicates)}build() {Stack({ alignContent: Alignment.BottomEnd }) {Column() {Stack() {if (this.state.isStage) {TitleBar()} else {TitleBar({ rightBtn: $r('app.media.more'), handleRightBtn: this.handleRightBtn })}if (this.state.isDistributed && !this.state.isStage && this.state.syncState === SyncState.ManualSync) {Row() {Blank()Image($r('app.media.ic_syncto')).size({ width: 50, height: 60 }).onClick(this.syncData)}.width('80%')}}.width('100%')SearchBar()List() {LazyForEach(this.contacts, (item: Contact, index) => {ListItem() {ContactItem({ contact: item, isMultiCheck: $isMultiCheck })}.onClick(() => {this.handleClickContact(item, index)})}, (item: Contact) => JSON.stringify(item))}.width('100%').layoutWeight(1).padding({ left: 10, right: 10 }).divider({ strokeWidth: 1, color: Color.Gray, startMargin: 16, endMargin: 16 })}.width('100%').height('100%')if (this.state.popupMenu) {PopupMenu({ state: $state, handleStartAbility: this.syncData })}BottomBtn({isMultiCheck: this.isMultiCheck,isSelectedAll: this.isSelectedAll,handleBottomBtnClick: this.handleBottomBtnClick})if (!this.isMultiCheck && !this.state.isStage) {Button() {Image($r('app.media.add')).height('100%').width('100%').objectFit(ImageFit.Contain).align(Alignment.End)}.id('btnAdd').width(80).height(80).margin({ right: 20, bottom: 50 }).type(ButtonType.Circle).backgroundColor('#0D9FFB').onClick(() => {Logger.info(TAG, 'onClick')router.pushUrl({url: 'pages/ContactEdit',params: { contact: new Contact(0, '', 0, '', -1, ''), isInsert: true }})})}}.width('100%').height('100%')}onBackPress() {Logger.info(TAG, 'onBackPress')let context = getContext(this) as common.UIAbilityContextcontext.startAbility({bundleName: BUNDLE,abilityName: ABILITY,deviceId: this.state.distributedDevice,parameters: {isStage: 'EXIT'}})this.rdbModel.offDataChange()}onPageHide() {Logger.info(TAG, 'onBackPress')clearInterval(this.intervalId)}}

使用[@ohos.distributedDeviceManager]接口,首先通过createDeviceManager创建设备管理器实例,然后通过getTrustedDeviceListSync同步获取所有可信设备列表;

  • 设备连接:首先通过on方法注册设备状态,例如发现设备,设备连接失败,然后通过startDeviceDiscovery方法发现周边设备,然后选择连接设备,再用[startAbility]启动连接设备的应用。

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

而网上有关鸿蒙的开发资料非常的少,假如你想学好鸿蒙的应用开发与系统底层开发。你可以参考这份资料,少走很多弯路,节省没必要的麻烦。由两位前阿里高级研发工程师联合打造《鸿蒙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/805717.shtml

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

相关文章

《QT实用小工具·十七》密钥生成工具

1、概述 源码放在文章末尾 该项目主要用于生成密钥&#xff0c;下面是demo演示&#xff1a; 项目部分代码如下&#xff1a; #pragma execution_character_set("utf-8")#include "frmmain.h" #include "ui_frmmain.h" #include "qmessag…

ArrayList中多线程的不安全问题

ArrayList中的不安全问题 正常的输出 List<String> list Arrays.asList("1","2","3"); list.forEach(System.out::println);为什么可以这样输出&#xff0c;是一种函数是接口&#xff0c;我们先过个耳熟 Arrys.asList是返回一个ArrayL…

进程间通信 (匿名管道)

一、进程间通信的概念 进程间通信是一个进程把自己的数据交给另一个进程&#xff0c;它可以帮助我们进行数据传输、资源共享、通知事件和进程控制。 进程间通信的本质是让不同的进程看到同一份资源。因此&#xff0c;我们要有&#xff1a; 1、交换数据的空间。2、这个空间不能由…

hadoop103: Permission denied (publickey,gssapi-keyex,gssapi-with-mic,password).

分析&#xff1a; 在启动hadoop服务的时候&#xff0c;遇到了这个问题&#xff1a; hadoop103: Permission denied (publickey,gssapi-keyex,gssapi-with-mic,password). 这个一看就是&#xff0c;密钥问题 于是ssh 主机名就行测试 需要输入密码&#xff0c;就说明这里有问…

C++笔记(函数重载)

目录 引入&#xff1a; 定义&#xff1a; 易错案例&#xff1a; 引入&#xff1a; 对于实现相似功能的函数&#xff0c;在命名时&#xff0c;我们常会出现命名重复的问题。对于C语言&#xff0c;编译器遇到这种命名重复的情况&#xff0c;会进行报错。而我们的C为了更方便程…

【计算机毕业设计】校园网书店系统——后附源码

&#x1f389;**欢迎来到我的技术世界&#xff01;**&#x1f389; &#x1f4d8; 博主小档案&#xff1a; 一名来自世界500强的资深程序媛&#xff0c;毕业于国内知名985高校。 &#x1f527; 技术专长&#xff1a; 在深度学习任务中展现出卓越的能力&#xff0c;包括但不限于…

分布式锁-redission

5、分布式锁-redission 5.1 分布式锁-redission功能介绍 基于setnx实现的分布式锁存在下面的问题&#xff1a; 重入问题&#xff1a;重入问题是指 获得锁的线程可以再次进入到相同的锁的代码块中&#xff0c;可重入锁的意义在于防止死锁&#xff0c;比如HashTable这样的代码…

pycharm一直打不开

一直处在下面的页面&#xff0c;没有反应 第一种方案&#xff1a; 以管理员身份运行 cmd.exe&#xff1b;在打开的cmd窗口中&#xff0c;输入 netsh winsock reset &#xff0c;按回车键&#xff1b;重启电脑&#xff1b;重启后&#xff0c;双击pycharm图标就能打开了&#xf…

深度理解运放增益带宽积

原文来自微信公众号&#xff1a;工程师看海&#xff0c;与我联系&#xff1a;chunhou0820 看海原创视频教程&#xff1a;《运放秘籍》 大家好&#xff0c;我是工程师看海。 增益带宽积是运算放大器的重要参数之一&#xff0c;指的是运放的增益和带宽的乘积&#xff0c;这个乘积…

STC89C52学习笔记(四)

STC89C52学习笔记&#xff08;四&#xff09; 综述&#xff1a;本文讲述了在STC89C51中数码管、模块化编程、LCD1602的使用。 一、数码管 1.数码管显示原理 位选&#xff1a;对74HC138芯片的输入端的配置&#xff08;P22、P23、P24&#xff09;&#xff0c;来选择实现位选&…

玩转ChatGPT:Kimi测评(图片识别)

一、写在前面 ChatGPT作为一款领先的语言模型&#xff0c;其强大的语言理解和生成能力&#xff0c;让无数用户惊叹不已。然而&#xff0c;使用的高门槛往往让国内普通用户望而却步。 最近&#xff0c;一款由月之暗面科技有限公司开发的智能助手——Kimi&#xff0c;很火爆哦。…

【Keil5-编译4个阶段】

Keil5-编译 ■ GCC编译4个阶段■ 预处理->编译->汇编->链接■ GNU工具链开发流程图■ armcc/armasm&#xff08;编译C和汇编&#xff09;■ armlink &#xff08;链接&#xff09;■ armar &#xff08;打包&#xff09;■ fromelf &#xff08;格式转换器&#xff09…

【LAMMPS学习】八、基础知识(1.7) LAMMPS 与 MDI 库代码耦合

8. 基础知识 此部分描述了如何使用 LAMMPS 为用户和开发人员执行各种任务。术语表页面还列出了 MD 术语&#xff0c;以及相应 LAMMPS 手册页的链接。 LAMMPS 源代码分发的 examples 目录中包含的示例输入脚本以及示例脚本页面上突出显示的示例输入脚本还展示了如何设置和运行各…

【现代C++】委托构造函数

现代C中的委托构造函数&#xff08;Delegating Constructors&#xff09;是C11引入的特性&#xff0c;它允许一个构造函数调用同一个类中的另一个构造函数&#xff0c;以避免代码重复。这种特性在初始化对象时提高了代码的复用性和清晰性。 1. 基本用法 在同一个类中&#xf…

(小红书平台)2024用户画像洞察报告

现今的小红书坐拥3亿月活用户&#xff0c;男女比例达到3:7&#xff0c;95后占比为50%&#xff0c;00后占比为35%&#xff0c;一二线城市用户占比50%。社区分享者超8000万&#xff0c;日均用户搜索渗透达到60%&#xff0c;UGC内容占比达90%。&#xff08;数据来源&#xff1a;小…

[lesson16]类的真正形态

类的真正形态 类的关键字 struct在C语言中以及有了自己的含义&#xff0c;必须继续兼容 在C中提供了新的关键字class用于类的定义 class和struct的用法是完全相同的 在用struct定义类时&#xff0c;所有成员的默认访问级别为public 在用class定义类时&#xff0c;所有成员…

虚拟网络设备的真正使命:实现有控制的通信

在数字化时代&#x1f4f2;&#xff0c;网络安全&#x1f512;成为了企业和个人防御体系中不可或缺的一部分。随着网络攻击的日益复杂和频繁&#x1f525;&#xff0c;传统的物理网络安全措施已经无法满足快速发展的需求。虚拟网络设备&#x1f5a7;&#xff0c;作为网络架构中…

谈谈功率IC巨头—士兰微

大家好&#xff0c;我是砖一。 今天给大家分享一下士兰微电子公司&#xff0c;&#xff0c;有做功率元器件&开关电源和IC的朋友可以了解一下&#xff0c;希望对你有用~ 1 公司介绍 士兰微电子成立于1997年&#xff0c;于2003年上市&#xff0c;总部位于杭州&#xff0c;…

unity按路径移动

using System; using System.Collections; using System.Collections.Generic; using UnityEngine;public class FollowPathMove : MonoBehaviour {public Transform[] wayPointArray;[SerializeField] private Transform PathA;//路径点的父物体[SerializeField]private Trans…

【春招面试篇】大厂面试干货分享

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学更多面经   &#x1f51d;&#x1f51d; 面试经验分享 1. 前言2. 去哪儿投简历?内推是否有用?3. 投递简历前的准备…