一、DataAbility基本概念

HarmonyOS 5中的DataAbility是一种特殊类型的Ability,主要用于提供统一的数据访问接口,实现应用间的数据共享。与UIAbility不同,DataAbility不直接与用户交互,而是专注于数据操作,为其他应用提供标准化的数据访问能力。

DataAbility的核心特点包括:

  1. 标准化接口:提供CRUD(创建、读取、更新、删除)等标准数据操作接口
  2. 跨进程通信:通过URI标识数据资源,支持跨应用访问
  3. 安全控制:通过权限机制保护敏感数据
  4. 多种数据源支持:可以封装SQLite数据库、文件系统等多种数据存储方式

二、DataAbility工作原理

DataAbility基于客户端-服务器模式工作:

  • 服务端:实现DataAbility的子类,重写数据操作方法
  • 客户端:通过DataAbilityHelper访问服务端提供的数据

通信过程使用URI标识数据资源,格式通常为:
dataability:///[authority]/[path]?[query]

三、完整示例:构建联系人DataAbility

下面我们实现一个完整的联系人管理DataAbility示例,包含服务端和客户端代码。

1. DataAbility服务端实现

首先创建ContactDataAbility.ts文件:

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

const CONTACTS_TABLE = 'contacts'
const DB_NAME = 'contact_db'
const STORE_CONFIG = {
  name: DB_NAME,
  securityLevel: relationalStore.SecurityLevel.S1
}

export default class ContactDataAbility extends dataAbility.DataAbility {
  private rdbStore: relationalStore.RdbStore | null = null

  // 初始化数据库
  async onInitialized(): Promise<void> {
    try {
      const sql = `CREATE TABLE IF NOT EXISTS ${CONTACTS_TABLE} (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        name TEXT NOT NULL,
        phone TEXT NOT NULL,
        email TEXT
      )`
      
      this.rdbStore = await relationalStore.getRdbStore(this.context, STORE_CONFIG)
      await this.rdbStore.executeSql(sql)
      console.info('Contact database initialized')
    } catch (err) {
      console.error(`Failed to initialize database: ${JSON.stringify(err)}`)
    }
  }

  // 插入联系人
  async insert(uri: string, valueBucket: dataAbility.ValuesBucket): Promise<number> {
    if (!this.rdbStore) {
      console.error('RdbStore is not initialized')
      return -1
    }

    try {
      const result = await this.rdbStore.insert(CONTACTS_TABLE, valueBucket)
      console.info(`Insert contact successfully, rowId: ${result}`)
      return result
    } catch (err) {
      console.error(`Failed to insert contact: ${JSON.stringify(err)}`)
      return -1
    }
  }

  // 查询联系人
  async query(
    uri: string,
    columns: Array<string>,
    predicates: dataAbility.DataAbilityPredicates
  ): Promise<dataAbility.ResultSet> {
    if (!this.rdbStore) {
      console.error('RdbStore is not initialized')
      return {} as dataAbility.ResultSet
    }

    try {
      const result = await this.rdbStore.query(
        predicates,
        columns
      )
      console.info('Query contacts successfully')
      return result
    } catch (err) {
      console.error(`Failed to query contacts: ${JSON.stringify(err)}`)
      return {} as dataAbility.ResultSet
    }
  }

  // 更新联系人
  async update(
    uri: string,
    valueBucket: dataAbility.ValuesBucket,
    predicates: dataAbility.DataAbilityPredicates
  ): Promise<number> {
    if (!this.rdbStore) {
      console.error('RdbStore is not initialized')
      return -1
    }

    try {
      const rowsAffected = await this.rdbStore.update(
        valueBucket,
        predicates
      )
      console.info(`Update contact successfully, rows affected: ${rowsAffected}`)
      return rowsAffected
    } catch (err) {
      console.error(`Failed to update contact: ${JSON.stringify(err)}`)
      return -1
    }
  }

  // 删除联系人
  async delete(
    uri: string,
    predicates: dataAbility.DataAbilityPredicates
  ): Promise<number> {
    if (!this.rdbStore) {
      console.error('RdbStore is not initialized')
      return -1
    }

    try {
      const rowsDeleted = await this.rdbStore.delete(
        predicates
      )
      console.info(`Delete contact successfully, rows deleted: ${rowsDeleted}`)
      return rowsDeleted
    } catch (err) {
      console.error(`Failed to delete contact: ${JSON.stringify(err)}`)
      return -1
    }
  }
}

2. 在module.json5中注册DataAbility

{
  "module": {
    "abilities": [
      {
        "name": "ContactDataAbility",
        "type": "data",
        "uri": "dataability:///com.example.contactdataability",
        "exported": true,
        "label": "Contact Data Ability",
        "icon": "$media:icon",
        "readPermission": "",
        "writePermission": "",
        "metadata": [
          {
            "name": "ohos.ability.dataability",
            "resource": "$profile:dataability_config"
          }
        ]
      }
    ]
  }
}

3. 客户端使用DataAbilityHelper访问数据

创建ContactManager.ts客户端代码:

// ContactManager.ts
import dataAbility from '@ohos.ability.dataAbility'
import relationalStore from '@ohos.data.relationalStore'

const CONTACT_URI = 'dataability:///com.example.contactdataability'

export class ContactManager {
  private dataAbilityHelper: dataAbility.DataAbilityHelper
  
  constructor(context: Context) {
    this.dataAbilityHelper = dataAbility.createDataAbilityHelper(context)
  }

  // 添加联系人
  async addContact(name: string, phone: string, email?: string): Promise<number> {
    const valueBucket: dataAbility.ValuesBucket = {
      'name': name,
      'phone': phone
    }
    
    if (email) {
      valueBucket['email'] = email
    }

    try {
      const result = await this.dataAbilityHelper.insert(CONTACT_URI, valueBucket)
      console.info(`Contact added successfully with ID: ${result}`)
      return result
    } catch (err) {
      console.error(`Failed to add contact: ${JSON.stringify(err)}`)
      return -1
    }
  }

  // 获取所有联系人
  async getAllContacts(): Promise<Array<Contact>> {
    const columns = ['id', 'name', 'phone', 'email']
    const predicates = new dataAbility.DataAbilityPredicates()
    predicates.equalTo('id', '*') // 查询所有记录

    try {
      const result = await this.dataAbilityHelper.query(
        CONTACT_URI,
        columns,
        predicates
      )
      
      const contacts: Array<Contact> = []
      if (result.rowCount > 0) {
        while (result.goToNextRow()) {
          contacts.push({
            id: result.getLong(result.getColumnIndex('id')),
            name: result.getString(result.getColumnIndex('name')),
            phone: result.getString(result.getColumnIndex('phone')),
            email: result.getString(result.getColumnIndex('email'))
          })
        }
      }
      
      console.info(`Retrieved ${contacts.length} contacts`)
      return contacts
    } catch (err) {
      console.error(`Failed to query contacts: ${JSON.stringify(err)}`)
      return []
    }
  }

  // 更新联系人
  async updateContact(
    id: number,
    updates: { name?: string; phone?: string; email?: string }
  ): Promise<boolean> {
    const valueBucket: dataAbility.ValuesBucket = {}
    
    if (updates.name) valueBucket['name'] = updates.name
    if (updates.phone) valueBucket['phone'] = updates.phone
    if (updates.email) valueBucket['email'] = updates.email

    const predicates = new dataAbility.DataAbilityPredicates()
    predicates.equalTo('id', id)

    try {
      const rowsAffected = await this.dataAbilityHelper.update(
        CONTACT_URI,
        valueBucket,
        predicates
      )
      
      console.info(`Updated ${rowsAffected} contact(s)`)
      return rowsAffected > 0
    } catch (err) {
      console.error(`Failed to update contact: ${JSON.stringify(err)}`)
      return false
    }
  }

  // 删除联系人
  async deleteContact(id: number): Promise<boolean> {
    const predicates = new dataAbility.DataAbilityPredicates()
    predicates.equalTo('id', id)

    try {
      const rowsDeleted = await this.dataAbilityHelper.delete(
        CONTACT_URI,
        predicates
      )
      
      console.info(`Deleted ${rowsDeleted} contact(s)`)
      return rowsDeleted > 0
    } catch (err) {
      console.error(`Failed to delete contact: ${JSON.stringify(err)}`)
      return false
    }
  }
}

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

4. UI界面调用DataAbility

创建ContactListPage.ets显示联系人列表:

// ContactListPage.ets
import { ContactManager } from '../model/ContactManager'

@Entry
@Component
struct ContactListPage {
  private contactManager: ContactManager = new ContactManager(getContext(this))
  @State contacts: Array<Contact> = []
  @State newName: string = ''
  @State newPhone: string = ''

  aboutToAppear() {
    this.loadContacts()
  }

  async loadContacts() {
    this.contacts = await this.contactManager.getAllContacts()
  }

  async addContact() {
    if (this.newName && this.newPhone) {
      await this.contactManager.addContact(this.newName, this.newPhone)
      this.newName = ''
      this.newPhone = ''
      await this.loadContacts()
    }
  }

  build() {
    Column() {
      // 添加联系人表单
      Row() {
        TextInput({ placeholder: 'Name', text: this.newName })
          .onChange((value: string) => {
            this.newName = value
          })
          .layoutWeight(1)
          .margin(5)

        TextInput({ placeholder: 'Phone', text: this.newPhone })
          .onChange((value: string) => {
            this.newPhone = value
          })
          .layoutWeight(1)
          .margin(5)

        Button('Add')
          .onClick(() => this.addContact())
          .margin(5)
      }
      .width('100%')
      .padding(10)

      // 联系人列表
      List({ space: 10 }) {
        ForEach(this.contacts, (contact: Contact) => {
          ListItem() {
            ContactItem({ contact: contact, onDeleted: () => this.loadContacts() })
          }
        }, (contact: Contact) => contact.id.toString())
      }
      .layoutWeight(1)
      .width('100%')
    }
    .height('100%')
    .width('100%')
  }
}

@Component
struct ContactItem {
  private contactManager: ContactManager = new ContactManager(getContext(this))
  @Prop contact: Contact
  @Link onDeleted: () => void

  async deleteContact() {
    const success = await this.contactManager.deleteContact(this.contact.id)
    if (success) {
      this.onDeleted()
    }
  }

  build() {
    Row() {
      Column() {
        Text(this.contact.name)
          .fontSize(18)
          .fontWeight(FontWeight.Bold)
        Text(this.contact.phone)
          .fontSize(16)
        if (this.contact.email) {
          Text(this.contact.email)
            .fontSize(14)
            .fontColor(Color.Gray)
        }
      }
      .layoutWeight(1)

      Button('Delete')
        .onClick(() => this.deleteContact())
        .backgroundColor(Color.Red)
        .fontColor(Color.White)
    }
    .width('100%')
    .padding(10)
    .borderRadius(5)
    .backgroundColor('#f5f5f5')
    .margin({ top: 5, bottom: 5 })
  }
}

四、DataAbility最佳实践

  1. 权限控制:为敏感数据设置读写权限
  2. 性能优化:批量操作减少跨进程通信次数
  3. 错误处理:全面捕获和处理可能的数据操作异常
  4. 数据验证:在DataAbility中验证输入数据的有效性
  5. 事务支持:对多个相关操作使用事务保证数据一致性

五、总结

HarmonyOS 5的DataAbility组件为应用间数据共享提供了安全高效的解决方案。通过标准化的接口和URI机制,开发者可以轻松实现跨应用数据访问,同时保持对数据安全的控制。本文展示的完整联系人管理示例演示了DataAbility的核心功能实现,可以作为开发实际项目的参考基础。

Logo

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

更多推荐