一、DataAbility基础概念

在HarmonyOS 5中,DataAbility是一种特殊类型的Ability,主要用于提供数据访问服务。它允许应用以统一的方式访问和操作数据,无论数据存储在SQLite数据库、文件系统还是其他位置。

DataAbility的核心特点包括:

  • 数据抽象:统一不同数据源的访问接口
  • 跨进程访问:支持应用间安全的数据共享
  • 标准化操作:提供CRUD(创建、读取、更新、删除)的标准接口

二、DataAbilityHelper介绍

DataAbilityHelper是HarmonyOS 5中用于与DataAbility交互的工具类,它封装了与DataAbility通信的细节,开发者可以通过它执行各种数据操作。

主要功能包括:

  • 插入(insert)
  • 删除(delete)
  • 更新(update)
  • 查询(query)
  • 批量操作(executeBatch)

三、完整示例:联系人管理应用

下面我们通过一个完整的联系人管理示例,演示如何使用DataAbilityHelper操作DataAbility。

1. 创建DataAbility

首先需要创建一个DataAbility来管理联系人数据:

// ContactDataAbility.ts
import relationalStore from '@ohos.data.relationalStore';
import dataAbility from '@ohos.ability.dataAbility';
import window from '@ohos.window';

const DB_NAME = 'contact_db'
const TABLE_NAME = 'contact'
const DB_VERSION = 1

let store: relationalStore.RdbStore | null = null

export default class ContactDataAbility extends dataAbility.DataAbility {
  async onInit(want: Want): Promise<void> {
    console.info('ContactDataAbility onInit')
    
    // 初始化数据库
    const config: relationalStore.StoreConfig = {
      name: DB_NAME,
      securityLevel: relationalStore.SecurityLevel.S1
    }
    
    try {
      store = await relationalStore.getRdbStore(this.context, config)
      await this.initTable()
    } catch (err) {
      console.error(`Failed to init RdbStore. Code:${err.code}, message:${err.message}`)
    }
  }

  private async initTable(): Promise<void> {
    // 创建联系人表
    const sql = `CREATE TABLE IF NOT EXISTS ${TABLE_NAME} (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      name TEXT NOT NULL,
      phone TEXT NOT NULL,
      email TEXT
    )`
    
    await store.executeSql(sql)
    console.info('Create table success')
  }

  // 插入联系人
  async insert(uri: string, valueBucket: ValuesBucket): Promise<number> {
    console.info('ContactDataAbility insert')
    if (!store) {
      console.error('RdbStore is not initialized')
      return -1
    }
    
    try {
      const id = await store.insert(TABLE_NAME, valueBucket)
      console.info(`Insert contact success, id:${id}`)
      return id
    } catch (err) {
      console.error(`Failed to insert contact. Code:${err.code}, message:${err.message}`)
      return -1
    }
  }

  // 查询联系人
  async query(uri: string, columns: Array<string>, predicates: dataAbility.DataAbilityPredicates): Promise<ResultSet> {
    console.info('ContactDataAbility query')
    if (!store) {
      console.error('RdbStore is not initialized')
      return null
    }
    
    try {
      const predicatesObj = predicates.getRdbPredicates(TABLE_NAME)
      const resultSet = await store.query(predicatesObj, columns)
      console.info('Query contact success')
      return resultSet
    } catch (err) {
      console.error(`Failed to query contact. Code:${err.code}, message:${err.message}`)
      return null
    }
  }

  // 更新联系人
  async update(uri: string, valueBucket: ValuesBucket, predicates: dataAbility.DataAbilityPredicates): Promise<number> {
    console.info('ContactDataAbility update')
    if (!store) {
      console.error('RdbStore is not initialized')
      return -1
    }
    
    try {
      const predicatesObj = predicates.getRdbPredicates(TABLE_NAME)
      const rowsAffected = await store.update(valueBucket, predicatesObj)
      console.info(`Update contact success, rows affected:${rowsAffected}`)
      return rowsAffected
    } catch (err) {
      console.error(`Failed to update contact. Code:${err.code}, message:${err.message}`)
      return -1
    }
  }

  // 删除联系人
  async delete(uri: string, predicates: dataAbility.DataAbilityPredicates): Promise<number> {
    console.info('ContactDataAbility delete')
    if (!store) {
      console.error('RdbStore is not initialized')
      return -1
    }
    
    try {
      const predicatesObj = predicates.getRdbPredicates(TABLE_NAME)
      const rowsAffected = await store.delete(predicatesObj)
      console.info(`Delete contact success, rows affected:${rowsAffected}`)
      return rowsAffected
    } catch (err) {
      console.error(`Failed to delete contact. Code:${err.code}, message:${err.message}`)
      return -1
    }
  }
}

2. 配置DataAbility

在module.json5中注册DataAbility:

{
  "module": {
    "abilities": [
      {
        "name": "ContactDataAbility",
        "type": "data",
        "uri": "dataability://com.example.contact.ContactDataAbility",
        "label": "Contact Data Ability",
        "icon": "$media:icon",
        "visible": true,
        "exported": true
      }
    ]
  }
}

3. 使用DataAbilityHelper访问数据

创建UI界面并使用DataAbilityHelper操作数据:

// ContactManager.ets
import dataAbility from '@ohos.ability.dataAbility';
import relationalStore from '@ohos.data.relationalStore';
import promptAction from '@ohos.promptAction';

@Entry
@Component
struct ContactManager {
  @State contacts: Array<Contact> = []
  private dataAbilityHelper: dataAbility.DataAbilityHelper | null = null
  private readonly CONTACT_URI = 'dataability://com.example.contact.ContactDataAbility/contact'

  aboutToAppear() {
    // 创建DataAbilityHelper
    this.dataAbilityHelper = dataAbility.createDataAbilityHelper(this.context)
    this.loadContacts()
  }

  // 加载联系人列表
  private loadContacts() {
    if (!this.dataAbilityHelper) return
    
    const predicates = new dataAbility.DataAbilityPredicates()
    predicates.orderByAsc('name')
    
    this.dataAbilityHelper.query(this.CONTACT_URI, ['id', 'name', 'phone', 'email'], predicates)
      .then((resultSet: relationalStore.ResultSet) => {
        this.contacts = []
        while (resultSet.goToNextRow()) {
          this.contacts.push({
            id: resultSet.getLong(resultSet.getColumnIndex('id')),
            name: resultSet.getString(resultSet.getColumnIndex('name')),
            phone: resultSet.getString(resultSet.getColumnIndex('phone')),
            email: resultSet.getString(resultSet.getColumnIndex('email'))
          })
        }
        resultSet.close()
      })
      .catch(err => {
        promptAction.showToast({ message: `查询失败: ${err.message}` })
      })
  }

  // 添加联系人
  private addContact(name: string, phone: string, email: string) {
    if (!this.dataAbilityHelper) return
    
    const valueBucket = {
      'name': name,
      'phone': phone,
      'email': email || ''
    }
    
    this.dataAbilityHelper.insert(this.CONTACT_URI, valueBucket)
      .then(id => {
        promptAction.showToast({ message: '添加成功' })
        this.loadContacts()
      })
      .catch(err => {
        promptAction.showToast({ message: `添加失败: ${err.message}` })
      })
  }

  // 更新联系人
  private updateContact(id: number, name: string, phone: string, email: string) {
    if (!this.dataAbilityHelper) return
    
    const valueBucket = {
      'name': name,
      'phone': phone,
      'email': email || ''
    }
    
    const predicates = new dataAbility.DataAbilityPredicates()
    predicates.equalTo('id', id)
    
    this.dataAbilityHelper.update(this.CONTACT_URI, valueBucket, predicates)
      .then(rowsAffected => {
        if (rowsAffected > 0) {
          promptAction.showToast({ message: '更新成功' })
          this.loadContacts()
        } else {
          promptAction.showToast({ message: '未找到联系人' })
        }
      })
      .catch(err => {
        promptAction.showToast({ message: `更新失败: ${err.message}` })
      })
  }

  // 删除联系人
  private deleteContact(id: number) {
    if (!this.dataAbilityHelper) return
    
    const predicates = new dataAbility.DataAbilityPredicates()
    predicates.equalTo('id', id)
    
    this.dataAbilityHelper.delete(this.CONTACT_URI, predicates)
      .then(rowsAffected => {
        if (rowsAffected > 0) {
          promptAction.showToast({ message: '删除成功' })
          this.loadContacts()
        } else {
          promptAction.showToast({ message: '未找到联系人' })
        }
      })
      .catch(err => {
        promptAction.showToast({ message: `删除失败: ${err.message}` })
      })
  }

  build() {
    Column() {
      List({ space: 10 }) {
        ForEach(this.contacts, (contact: Contact) => {
          ListItem() {
            ContactItem({ contact: contact, 
              onDelete: (id: number) => this.deleteContact(id) 
            })
          }
        }, (contact: Contact) => contact.id.toString())
      }
      .layoutWeight(1)
      .width('100%')
      
      Button('添加测试联系人')
        .onClick(() => {
          this.addContact('测试用户', '13800138000', 'test@example.com')
        })
        .margin(10)
    }
    .width('100%')
    .height('100%')
    .padding(10)
  }
}

@Component
struct ContactItem {
  @Prop contact: Contact
  @Link onDelete: (id: number) => void
  
  @State showDialog: boolean = false

  build() {
    Column() {
      Text(this.contact.name)
        .fontSize(20)
        .fontWeight(FontWeight.Bold)
      Text(`电话: ${this.contact.phone}`)
      Text(`邮箱: ${this.contact.email || '无'}`)
      
      Row() {
        Button('删除')
          .onClick(() => {
            this.showDialog = true
          })
      }
      .justifyContent(FlexAlign.End)
      .width('100%')
    }
    .padding(10)
    .borderRadius(10)
    .backgroundColor(Color.White)
    .shadow({ radius: 5, color: '#ccc', offsetX: 2, offsetY: 2 })
    
    // 删除确认对话框
    if (this.showDialog) {
      DialogComponent({
        title: '确认删除',
        message: `确定要删除联系人 ${this.contact.name} 吗?`,
        onConfirm: () => {
          this.onDelete(this.contact.id)
          this.showDialog = false
        },
        onCancel: () => {
          this.showDialog = false
        }
      })
    }
  }
}

// 对话框组件
@Builder
function DialogComponent(params: {
  title: string,
  message: string,
  onConfirm: () => void,
  onCancel: () => void
}) {
  Column() {
    Text(params.title)
      .fontSize(20)
      .fontWeight(FontWeight.Bold)
      .margin({ bottom: 10 })
    
    Text(params.message)
      .margin({ bottom: 20 })
    
    Row() {
      Button('取消')
        .onClick(params.onCancel)
        .layoutWeight(1)
      
      Button('确定')
        .onClick(params.onConfirm)
        .layoutWeight(1)
    }
    .width('100%')
  }
  .padding(20)
  .backgroundColor(Color.White)
  .borderRadius(10)
}

interface Contact {
  id: number
  name: string
  phone: string
  email: string
}

四、DataAbility高级用法

1. 批量操作

DataAbilityHelper提供了executeBatch方法用于执行批量操作:

private batchInsertContacts(contacts: Array<Omit<Contact, 'id'>>) {
  if (!this.dataAbilityHelper) return
  
  const operations: Array<dataAbility.DataAbilityOperation> = contacts.map(contact => {
    return {
      uri: this.CONTACT_URI,
      type: dataAbility.OperationType.INSERT,
      valuesBucket: {
        'name': contact.name,
        'phone': contact.phone,
        'email': contact.email || ''
      }
    }
  })
  
  this.dataAbilityHelper.executeBatch(this.CONTACT_URI, operations)
    .then(results => {
      const successCount = results.filter(r => r.count > 0).length
      promptAction.showToast({ message: `批量添加成功 ${successCount}/${contacts.length}` })
      this.loadContacts()
    })
    .catch(err => {
      promptAction.showToast({ message: `批量添加失败: ${err.message}` })
    })
}

2. 文件操作

DataAbility也可以用于文件操作,例如:

// 在DataAbility中实现文件操作
async fileInsert(uri: string, valueBucket: ValuesBucket): Promise<number> {
  const fileUri = valueBucket.get('fileUri')
  const fileName = valueBucket.get('fileName')
  
  try {
    const file = await fs.open(fileUri, fs.OpenMode.READ_ONLY)
    const content = await fs.readText(file.fd)
    await fs.close(file.fd)
    
    const targetPath = this.context.filesDir + '/' + fileName
    const targetFile = await fs.open(targetPath, fs.OpenMode.WRITE_ONLY | fs.OpenMode.CREATE)
    await fs.write(targetFile.fd, content)
    await fs.close(targetFile.fd)
    
    return 1
  } catch (err) {
    console.error(`File operation failed: ${err.message}`)
    return -1
  }
}

// 客户端调用
private saveFile(uri: string, fileName: string) {
  const valueBucket = {
    'fileUri': uri,
    'fileName': fileName
  }
  
  this.dataAbilityHelper.insert('dataability://com.example.file.FileDataAbility/file', valueBucket)
    .then(result => {
      promptAction.showToast({ message: '文件保存成功' })
    })
    .catch(err => {
      promptAction.showToast({ message: `文件保存失败: ${err.message}` })
    })
}

五、最佳实践与注意事项

  1. 性能优化

    • 对于批量操作,优先使用executeBatch
    • 查询时只获取需要的列
    • 使用分页查询大数据集
  2. 安全考虑

    • 验证输入数据
    • 使用参数化查询防止SQL注入
    • 设置合适的权限控制
  3. 错误处理

    • 检查DataAbilityHelper是否初始化
    • 捕获并处理所有可能的异常
    • 提供有意义的错误反馈
  4. 线程管理

    • 数据操作应在非UI线程执行
    • 使用Promise或async/await处理异步操作

通过本文的介绍和示例,您应该已经掌握了HarmonyOS 5中DataAbility的基本用法和高级技巧。DataAbility提供了一种统一、安全的数据访问方式,是HarmonyOS应用开发中不可或缺的重要组成部分。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐