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

news2025/9/21 22:14:00

 

介绍

本示例使用[@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 = undefined

  private tableName: string = ''

  private sqlCreateTable: string = ''

  private columns: Array<string> = []

  private distributedTable: string = ''

  private dataChangeCallback : Function| null = null

  private isCreateDbDone: boolean = false

  private context: common.UIAbilityContext



  constructor(tableName: string, sqlCreateTable: string, columns: Array<string>, context: common.UIAbilityContext) {

    this.tableName = tableName

    this.sqlCreateTable = sqlCreateTable

    this.columns = columns

    this.context = context



    this.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);

        // 设置分布式表,表明为contact

        await this.rdbStore.setDistributedTables([this.tableName])

      }

    } catch (e) {

      Logger.error(TAG, 'getRdbStore:' + JSON.stringify(e))

    }

    // 分布式数据库创建为完成

    this.isCreateDbDone = true

    Logger.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 = callback

    await 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) {

      // 读取各个属性,初始化临时变量contact

      let 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 = 100

const 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 = undefined



  registerDeviceListCallback(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] = device

    Logger.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 = list

    Logger.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] = device

        break

      }

    }

    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 = callback

    if (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)

          break

        case deviceManager.DeviceStateChange.AVAILABLE:

          this.deviceStateChangeActionReady(data.device)

          break

        case deviceManager.DeviceStateChange.UNAVAILABLE:

          this.deviceStateChangeActionOffline(data.device)

          break

        default:

          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.device

        Logger.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

@Component

struct 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 Want

    if(want.parameters != undefined) {

      if (this.state.isDistributed && want.parameters.isStage === 'EXIT') {

        Logger.info(TAG, 'isStage = EXIT')

        this.state.isStage = false

        this.state.isDistributed = false

        this.state.selectedIndex = 0

        this.state.distributedDevice = ''

        this.rdbModel.offDataChange()

      }

      if (!this.state.isDistributed && want.parameters.isStage === 'Stage') {

        Logger.info(TAG, 'isStage = Stage')

        this.state.isStage = true

        this.state.isDistributed = true

        this.state.distributedDevice = want.parameters.dmsSrcNetworkId as string;

        let context = getContext(this) as common.UIAbilityContext

        context.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 = result

          this.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')

    // 初始化数据库的表,表名为contact

    let 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.dataArray

      this.contacts.dataArray = []

      tempContacts[index].isSelected = !item.isSelected

      this.contacts.dataArray = tempContacts

      this.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 = false

    this.refreshSelectState(this.isSelectedAll)

    this.isMultiCheck = false

  }



  handleBottomBtnClick = (index: number) => {

    switch (index) {

      case 0:

        this.isSelectedAll = !this.isSelectedAll

        this.refreshSelectState(this.isSelectedAll)

        break

      case 1:

        this.showDeleteDialog()

        break

      case 2:

        this.quitMultiCheck()

        break

      default:

        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.UIAbilityContext

    context.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.coloradmin.cn/o/1583864.html

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

相关文章

实验1 eNSP安装与使用

实验1 eNSP安装与使用 一、 原理描述二、 实验目的三、 实验内容四、 实验步骤1.下载并安装eNSP2.eNSP软件界面3.搭建并运行网络拓扑4. Wireshark 捕获分组并分析 一、 原理描述 eNSP&#xff08;Enterprise Network Simulation Platform&#xff09;是由华为提供的免费网络模…

TrollInstallerX官方一键安装巨魔商店

TrollInstallerX是巨魔官方开发的一款一键巨魔商店安装器&#xff0c;完美支持iOS 14.0 – 16.6.1的设备&#xff0c;操作非常简单&#xff0c;TrollInstallerX依然有个小小的限制&#xff0c;部分机型&#xff0c;还是要采用间接安装方法。 一&#xff0c;直接安装方法 通过…

探索进程控制第一弹(进程终止、进程等待)

文章目录 进程创建初识fork函数fork函数返回值fork常规用法fork调用失败的原因 写时拷贝进程终止进程终止是在做什么&#xff1f;进程终止的情况代码跑完&#xff0c;结果正确/不正确代码异常终止 如何终止 进程等待概述进程等待方法wait方法waitpid 进程创建 初识fork函数 在…

C++ 线程库(thread)与锁(mutex)

一.线程库(thread) 1.1 线程类的简单介绍 thread类文档介绍 在C11之前&#xff0c;涉及到多线程问题&#xff0c;都是和平台相关的&#xff0c;比如windows和linux下各有自己的接口&#xff0c;这使得代码的可移植性比较差。C11中最重要的特性就是对线程进行支持了&#xff…

数据结构与算法——字符串暴力匹配

一、字符串的组成 1.数据域&#xff0c;字符串的内容 2.字符串的长度 二、模式匹配-暴力匹配原理 1.两个字符串一个主串一个模式串用两个指针对其进行匹配 2、两个指针所对应的值相同时继续匹配下一个 3、当出现不匹配的情况时&#xff0c;回溯主串的指针到刚开始起点的下一个位…

IP地址定位技术在各领域的作用

IP地址定位是通过确定IP地址的物理位置来定位一个设备的技术&#xff0c;它在现代社会的多个领域中都有着广泛的应用。以下将详细探讨IP地址定位的应用场景&#xff0c;以期对读者有所启发。 首先&#xff0c;在网络安全领域&#xff0c;IP地址定位发挥着至关重要的作用。网络…

业务逻辑漏洞(靶场) fiddler

目录 fiddler简介&#xff1a; 业务逻辑漏洞&#xff1a; fiddler下载 靶场&#xff1a; 实验一 ​编辑实验二&#xff08;ps 更改实验url会变&#xff0c;fiddler没抓到东西看看代理改没改&#xff09; 实验三 实验四 fiddler简介&#xff1a; 一款网络抓包工具&#…

如何丝滑地切换node版本

背景 由于有些比较老的系统&#xff0c;使用的node版本较低&#xff0c;本机装的node版本又比较高&#xff0c;不想降node版本&#xff0c;那么就需要考虑能不能在系统里管理多个node版本呢&#xff1f;由于我使用的操作系统是Mac&#xff0c;下面将主要讲解如何在Mac上快速切…

Tmux 使用笔记

Tmux 是一个终端复用器&#xff08;terminal multiplexer&#xff09;&#xff0c;非常有用&#xff0c;属于常用的开发工具。 本文记录个人使用 Tmux的命令。 1. tmux简介 命令行的典型使用方式是&#xff0c;打开一个终端窗口&#xff0c;连接计算机&#xff0c;在里面输入…

LeetCode-热题100:148. 排序链表

题目描述 给你链表的头结点 head &#xff0c;请将其按 升序 排列并返回 排序后的链表 。 示例 1&#xff1a; 输入&#xff1a; head [4,2,1,3] 输出&#xff1a; [1,2,3,4] 示例 2&#xff1a; 输入&#xff1a; head [-1,5,3,4,0] 输出&#xff1a; [-1,0,3,4,5] 示例…

探索未来产业:新技术、新商业、新趋势

引言 随着科技的迅速发展和全球经济的不断变化&#xff0c;未来产业已经成为全球关注的焦点之一。未来产业的兴起不仅代表着新的商业机遇&#xff0c;更是对传统产业模式的颠覆和重构。在这个充满挑战和机遇的时代&#xff0c;我们不得不认真思考未来产业的重要性和前景。 未…

现在软考考什么科目最好?

适合你的学科才是最适合你的学科。 在软考的同一级别&#xff0c;证书的价值差不多。只能说有些学科比较受欢迎&#xff0c;需求量比较大。 中级的系统集成项目管理工程师是最受欢迎的考试科目&#xff0c;其次是软件设计师和网络工程师。由于市场需求大&#xff0c;报考人数也…

gradio简单搭建——关键词匹配筛选【进一步优化】

gradio简单搭建——关键词匹配筛选[进一步优化] 任务回顾新的想法&#xff1a;无效元素筛选界面搭建数据处理与生成过程交互界面展示 任务回顾 在 apply \text{apply} apply方法的使用一节中&#xff0c;简单提到了任务目标&#xff1a;通过关键词的形式&#xff0c;在文本数据…

DNS 各记录类型说明及规则

各记录类型使用目的 记录类型使用目的A 记录将域名指向一个 IP 地址。CNAME 记录将域名指向另一个域名&#xff0c;再由另一个域名提供 IP 地址。MX 记录设置邮箱&#xff0c;让邮箱能收到邮件。NS 记录将子域名交给其他 DNS 服务商解析。AAAA 记录将域名指向一个 IPv6 地址。…

VMware vSphere虚拟化基础管理平台

VMware简介 VMware介绍 官网&#xff1a;https://www.vmware.com/cn.html VMware公司成立于1998年&#xff0c;2003年存储厂商EMC以6.35亿美元收购了VMware&#xff1b;2015年10月&#xff0c;戴尔宣布以670亿美元收购EMC。VMware公司在2018年全年收入79.2亿美元。 VMware主…

20240326-1-KNN面试题

KNN面试题 1.简述一下KNN算法的原理 KNN算法利用训练数据集对特征向量空间进行划分。KNN算法的核心思想是在一个含未知样本的空间&#xff0c;可以根据样本最近的k个样本的数据类型来确定未知样本的数据类型。 该算法涉及的3个主要因素是&#xff1a;k值选择&#xff0c;距离度…

AOF文件重写

1.2.3.AOF文件重写 因为是记录命令&#xff0c;AOF文件会比RDB文件大的多。而且AOF会记录对同一个key的多次写操作&#xff0c;但只有最后一次写操作才有意义。通过执行bgrewriteaof命令&#xff0c;可以让AOF文件执行重写功能&#xff0c;用最少的命令达到相同效果。 如图&am…

SolidWorks上手直接开整

本科学3D建模的时候&#xff0c;学习过犀牛Rhine&#xff0c;当时就惊叹于入籍紧密集成的可视化编程环境&#xff0c;后来又简单学习过了3DMax、Maya&#xff0c;平时使用的频率不多&#xff0c;所以还是初学者水平&#xff0c;现在课题组需要自研一个原型机&#xff0c;需要工…

图片管理系统:原理、设计与实践

title: 图片管理系统&#xff1a;原理、设计与实践 date: 2024/4/9 20:04:25 updated: 2024/4/9 20:04:25 tags: 图片管理存储组织上传采集处理编辑搜索检索展示分享AI应用 第一章&#xff1a;图片管理系统概述 1.1 图片管理系统简介 图片管理系统是一种用于存储、组织、处理…

Redux和Redux Toolkit

Redux 概念&#xff1a;redux是react最常用的集中状态管理工具&#xff0c;类似于Vue中的Pinia(vuex)&#xff0c;可以独立于框架运行作用&#xff1a;通过集中管理的方式管理应用的状态 Redux快速体验 不和任何框架绑定&#xff0c;不使用任何构建工具&#xff0c;使用纯Re…