Skip to content

数据存储

HarmonyOS 提供多种数据存储方案,包括用户首选项(Preferences)、关系型数据库(RDB)、键值型数据库(KV-Store)以及文件存储,满足不同场景需求。

存储方案对比

方案数据类型容量适用场景起始版本
Preferences键值对小(建议 < 1MB)用户设置、配置项API 9
RDB结构化数据复杂数据、需要查询API 9
KV-Store键值对分布式数据同步API 9
文件任意图片、日志、缓存API 6

用户首选项(Preferences)

轻量级键值对存储,适合保存用户设置等小数据。数据存储在 XML 文件中,支持布尔、数值、字符串类型。

API 列表

API说明起始版本
preferences.getPreferences()获取 Preferences 实例API 9
Preferences.put()写入键值对API 9
Preferences.get()读取键值对API 9
Preferences.delete()删除键值对API 9
Preferences.flush()将数据持久化到磁盘API 9
Preferences.clear()清空所有数据API 9
preferences.deletePreferences()删除 Preferences 实例API 9

ValueType 类型

类型说明
number数值类型
string字符串类型
boolean布尔类型

基础用法

typescript
import { preferences } from '@kit.ArkData'

class PreferenceUtil {
  private static instance: PreferenceUtil
  private pref: preferences.Preferences | null = null

  static async getInstance(): Promise<PreferenceUtil> {
    if (!PreferenceUtil.instance) {
      PreferenceUtil.instance = new PreferenceUtil()
      const context = getContext()
      PreferenceUtil.instance.pref = await preferences.getPreferences(context, 'my_app_prefs')
    }
    return PreferenceUtil.instance
  }

  async put(key: string, value: preferences.ValueType): Promise<void> {
    if (this.pref) {
      await this.pref.put(key, value)
      await this.pref.flush()  // 必须 flush 才落盘
    }
  }

  async get(key: string, defaultValue: preferences.ValueType): Promise<preferences.ValueType> {
    if (this.pref) {
      return await this.pref.get(key, defaultValue)
    }
    return defaultValue
  }

  async delete(key: string): Promise<void> {
    if (this.pref) {
      await this.pref.delete(key)
      await this.pref.flush()
    }
  }

  async clear(): Promise<void> {
    if (this.pref) {
      await this.pref.clear()
      await this.pref.flush()
    }
  }
}

// 使用示例
async function demo() {
  const pref = await PreferenceUtil.getInstance()

  // 保存数据
  await pref.put('username', '张三')
  await pref.put('isVip', true)
  await pref.put('level', 5)

  // 读取数据
  const username = await pref.get('username', '') as string
  const isVip = await pref.get('isVip', false) as boolean
  const level = await pref.get('level', 0) as number

  console.info(`用户: ${username}, VIP: ${isVip}, 等级: ${level}`)

  // 删除数据
  await pref.delete('temp_data')
}

数据变更监听

typescript
import { preferences } from '@kit.ArkData'

async function watchPreferenceChanges() {
  const context = getContext()
  const pref = await preferences.getPreferences(context, 'my_app_prefs')

  // 注册数据变更监听
  pref.on('change', (key: string) => {
    console.info(`键 ${key} 的值发生变化`)
  })

  // 写入数据会触发监听
  await pref.put('theme', 'dark')
  await pref.flush()

  // 取消监听
  // pref.off('change')
}

关系型数据库(RDB)

基于 SQLite 的封装,适合结构化数据存储。支持 SQL 语句、事务、索引等高级功能。

API 列表

API说明起始版本
relationalStore.getRdbStore()获取 RDB 实例API 9
RdbStore.executeSql()执行 SQL 语句API 9
RdbStore.insert()插入数据API 9
RdbStore.update()更新数据API 9
RdbStore.delete()删除数据API 9
RdbStore.query()条件查询API 9
RdbStore.querySql()SQL 查询API 9
RdbStore.beginTransaction()开始事务API 9
RdbStore.commit()提交事务API 9
RdbStore.rollBack()回滚事务API 9

SecurityLevel 枚举

枚举值说明
S1数据库在公共目录下,没有安全保护
S2数据库在应用私有目录下,有基本安全保护
S3数据库加密,有较高安全保护
S4数据库加密,有最高安全保护

基础用法

typescript
import { relationalStore } from '@kit.ArkData'

class DatabaseHelper {
  private static store: relationalStore.RdbStore | null = null

  static async init(): Promise<void> {
    const context = getContext()
    const config: relationalStore.StoreConfig = {
      name: 'app.db',
      securityLevel: relationalStore.SecurityLevel.S1
    }

    this.store = await relationalStore.getRdbStore(context, config)

    // 创建表
    await this.store.executeSql(`
      CREATE TABLE IF NOT EXISTS users (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        name TEXT NOT NULL,
        email TEXT,
        age INTEGER,
        created_at INTEGER
      )
    `)
  }

  static async insertUser(user: Omit<User, 'id'>): Promise<number> {
    if (!this.store) throw new Error('Database not initialized')

    const values = {
      name: user.name,
      email: user.email,
      age: user.age,
      created_at: Date.now()
    }

    return await this.store.insert('users', values)
  }

  static async getUsers(): Promise<User[]> {
    if (!this.store) throw new Error('Database not initialized')

    const resultSet = await this.store.querySql('SELECT * FROM users ORDER BY created_at DESC')
    const users: User[] = []

    while (resultSet.goToNextRow()) {
      users.push({
        id: resultSet.getLong(resultSet.getColumnIndex('id')),
        name: resultSet.getString(resultSet.getColumnIndex('name')),
        email: resultSet.getString(resultSet.getColumnIndex('email')),
        age: resultSet.getLong(resultSet.getColumnIndex('age')),
        createdAt: resultSet.getLong(resultSet.getColumnIndex('created_at'))
      })
    }

    resultSet.close()
    return users
  }

  static async updateUser(id: number, values: Partial<User>): Promise<void> {
    if (!this.store) throw new Error('Database not initialized')

    const updateValues: relationalStore.ValuesBucket = {}
    if (values.name) updateValues.name = values.name
    if (values.email) updateValues.email = values.email
    if (values.age) updateValues.age = values.age

    const predicates = new relationalStore.RdbPredicates('users')
    predicates.equalTo('id', id)

    await this.store.update(updateValues, predicates)
  }

  static async deleteUser(id: number): Promise<void> {
    if (!this.store) throw new Error('Database not initialized')

    const predicates = new relationalStore.RdbPredicates('users')
    predicates.equalTo('id', id)

    await this.store.delete(predicates)
  }

  static async searchUsers(keyword: string): Promise<User[]> {
    if (!this.store) throw new Error('Database not initialized')

    const predicates = new relationalStore.RdbPredicates('users')
    predicates.contains('name', keyword)
      .or()
      .contains('email', keyword)

    const resultSet = await this.store.query(predicates, ['id', 'name', 'email', 'age', 'created_at'])
    const users: User[] = []

    while (resultSet.goToNextRow()) {
      users.push({
        id: resultSet.getLong(resultSet.getColumnIndex('id')),
        name: resultSet.getString(resultSet.getColumnIndex('name')),
        email: resultSet.getString(resultSet.getColumnIndex('email')),
        age: resultSet.getLong(resultSet.getColumnIndex('age')),
        createdAt: resultSet.getLong(resultSet.getColumnIndex('created_at'))
      })
    }

    resultSet.close()
    return users
  }

  static async close(): Promise<void> {
    if (this.store) {
      await this.store.close()
      this.store = null
    }
  }
}

interface User {
  id: number
  name: string
  email: string
  age: number
  createdAt: number
}

事务处理

typescript
import { relationalStore } from '@kit.ArkData'

async function batchInsertUsers(users: Omit<User, 'id'>[]): Promise<void> {
  const store = await relationalStore.getRdbStore(getContext(), {
    name: 'app.db',
    securityLevel: relationalStore.SecurityLevel.S1
  })

  try {
    store.beginTransaction()

    for (const user of users) {
      await store.insert('users', {
        name: user.name,
        email: user.email,
        age: user.age,
        created_at: Date.now()
      })
    }

    store.commit()
    console.info('批量插入成功')
  } catch (error) {
    store.rollBack()
    console.error('批量插入失败,已回滚:', JSON.stringify(error))
    throw error
  }
}

RdbPredicates 条件构造

typescript
import { relationalStore } from '@kit.ArkData'

// 等于
const p1 = new relationalStore.RdbPredicates('users')
p1.equalTo('age', 18)

// 不等于
const p2 = new relationalStore.RdbPredicates('users')
p2.notEqualTo('status', 'deleted')

// 大于/小于
const p3 = new relationalStore.RdbPredicates('users')
p3.greaterThan('age', 18)
  .lessThan('age', 60)

// 包含
const p4 = new relationalStore.RdbPredicates('users')
p4.contains('name', '张')

// 以...开头
const p5 = new relationalStore.RdbPredicates('users')
p5.beginsWith('email', 'admin')

// 组合条件
const p6 = new relationalStore.RdbPredicates('users')
p6.equalTo('status', 'active')
  .and()
  .greaterThan('age', 18)

// 排序
const p7 = new relationalStore.RdbPredicates('users')
p7.orderByAsc('age')
  .orderByDesc('created_at')

// 限制数量
const p8 = new relationalStore.RdbPredicates('users')
p8.limit(10)
  .offset(20)

键值型数据库(KV-Store)

适合需要跨设备同步的键值对数据存储场景。

API 列表

API说明起始版本
distributedKVManager.getKVManager()获取 KVManager 实例API 9
KVManager.getKVStore()获取 KVStore 实例API 9
SingleKVStore.put()写入键值对API 9
SingleKVStore.get()读取键值对API 9
SingleKVStore.delete()删除键值对API 9
SingleKVStore.on('dataChange')监听数据变化API 9

基础用法

typescript
import { distributedKVManager } from '@kit.ArkData'

class KVStoreUtil {
  private static kvManager: distributedKVManager.KVManager | null = null
  private static kvStore: distributedKVManager.SingleKVStore | null = null

  static async init(): Promise<void> {
    const context = getContext()
    const config: distributedKVManager.KVManagerConfig = {
      bundleName: context.applicationInfo.name,
      context
    }

    this.kvManager = distributedKVManager.getKVManager(config)

    const storeConfig: distributedKVManager.Options = {
      createIfMissing: true,
      encrypt: false,
      backup: false,
      autoSync: true,
      kvStoreType: distributedKVManager.KVStoreType.SINGLE_VERSION,
      securityLevel: distributedKVManager.SecurityLevel.S1
    }

    this.kvStore = await this.kvManager.getKVStore('my_kv_store', storeConfig)
  }

  static async put(key: string, value: Uint8Array | string | number | boolean): Promise<void> {
    if (!this.kvStore) throw new Error('KVStore not initialized')
    await this.kvStore.put(key, value)
  }

  static async get(key: string): Promise<Uint8Array | string | number | boolean | undefined> {
    if (!this.kvStore) throw new Error('KVStore not initialized')
    return await this.kvStore.get(key)
  }

  static async delete(key: string): Promise<void> {
    if (!this.kvStore) throw new Error('KVStore not initialized')
    await this.kvStore.delete(key)
  }

  static async close(): Promise<void> {
    if (this.kvManager && this.kvStore) {
      await this.kvManager.closeKVStore('my_kv_store')
      this.kvStore = null
    }
  }
}

文件存储

应用沙箱目录读写,无需权限。适合存储图片、日志、缓存等大文件。

应用沙箱目录

目录路径获取说明
filesDircontext.filesDir应用文件目录,持久存储
cacheDircontext.cacheDir缓存目录,系统可能自动清理
tempDircontext.tempDir临时目录
databaseDircontext.databaseDir数据库目录

API 列表

API说明起始版本
fileIo.openSync()打开文件API 9
fileIo.writeSync()写入文件API 9
fileIo.readSync()读取文件API 9
fileIo.closeSync()关闭文件API 9
fileIo.unlinkSync()删除文件API 9
fileIo.accessSync()检查文件是否存在API 9
fileIo.statSync()获取文件状态API 9
fileIo.mkdirSync()创建目录API 9

OpenMode 枚举

枚举值说明
READ_ONLY只读模式
WRITE_ONLY只写模式
READ_WRITE读写模式
CREATE创建文件
TRUNC截断文件
APPEND追加模式

基础用法

typescript
import { fileIo } from '@kit.CoreFileKit'

class FileUtil {
  // 获取应用文件目录
  static getFilesDir(): string {
    return getContext().filesDir
  }

  // 写入文本文件
  static writeText(fileName: string, content: string): void {
    const path = `${this.getFilesDir()}/${fileName}`
    const file = fileIo.openSync(path, fileIo.OpenMode.CREATE | fileIo.OpenMode.WRITE_ONLY)
    fileIo.writeSync(file.fd, content)
    fileIo.closeSync(file)
  }

  // 读取文本文件
  static readText(fileName: string): string {
    const path = `${this.getFilesDir()}/${fileName}`
    const file = fileIo.openSync(path, fileIo.OpenMode.READ_ONLY)
    const stat = fileIo.statSync(file.fd)
    const buf = new ArrayBuffer(stat.size)
    fileIo.readSync(file.fd, buf)
    fileIo.closeSync(file)
    return bufferToString(buf)
  }

  // 追加写入
  static appendText(fileName: string, content: string): void {
    const path = `${this.getFilesDir()}/${fileName}`
    const file = fileIo.openSync(path, fileIo.OpenMode.APPEND | fileIo.OpenMode.WRITE_ONLY)
    fileIo.writeSync(file.fd, content)
    fileIo.closeSync(file)
  }

  // 检查文件是否存在
  static exists(fileName: string): boolean {
    try {
      const path = `${this.getFilesDir()}/${fileName}`
      fileIo.accessSync(path)
      return true
    } catch {
      return false
    }
  }

  // 删除文件
  static delete(fileName: string): void {
    const path = `${this.getFilesDir()}/${fileName}`
    fileIo.unlinkSync(path)
  }

  // 创建目录
  static mkdir(dirName: string): void {
    const path = `${this.getFilesDir()}/${dirName}`
    fileIo.mkdirSync(path)
  }

  // 获取文件大小
  static getSize(fileName: string): number {
    const path = `${this.getFilesDir()}/${fileName}`
    const file = fileIo.openSync(path, fileIo.OpenMode.READ_ONLY)
    const stat = fileIo.statSync(file.fd)
    fileIo.closeSync(file)
    return stat.size
  }
}

// 辅助函数:ArrayBuffer 转字符串
function bufferToString(buffer: ArrayBuffer): string {
  const decoder = new TextDecoder('utf-8')
  return decoder.decode(buffer)
}

// 辅助函数:字符串转 ArrayBuffer
function stringToBuffer(str: string): ArrayBuffer {
  const encoder = new TextEncoder()
  return encoder.encode(str).buffer
}

// 使用示例
function demo() {
  // 保存 JSON 数据
  const data = { name: '张三', age: 25 }
  FileUtil.writeText('user.json', JSON.stringify(data))

  // 读取 JSON 数据
  const json = FileUtil.readText('user.json')
  const user = JSON.parse(json)
  console.info('用户:', JSON.stringify(user))

  // 追加日志
  FileUtil.appendText('app.log', `[${new Date().toISOString()}] 应用启动\n`)

  // 检查文件
  if (FileUtil.exists('user.json')) {
    console.info('文件存在,大小:', FileUtil.getSize('user.json'))
  }

  // 删除文件
  FileUtil.delete('user.json')
}

完整示例:用户数据管理

typescript
import { preferences } from '@kit.ArkData'
import { relationalStore } from '@kit.ArkData'

@Entry
@Component
struct StorageDemo {
  @State username: string = ''
  @State users: User[] = []

  aboutToAppear() {
    this.loadData()
  }

  async loadData() {
    // 从 Preferences 读取
    const pref = await PreferenceUtil.getInstance()
    const savedName = await pref.get('lastUser', '') as string
    this.username = savedName

    // 从数据库读取
    await DatabaseHelper.init()
    this.users = await DatabaseHelper.getUsers()
  }

  async addUser() {
    if (!this.username) return

    // 保存到数据库
    const id = await DatabaseHelper.insertUser({
      name: this.username,
      email: `${this.username}@example.com`,
      age: 25
    })

    // 保存到 Preferences
    const pref = await PreferenceUtil.getInstance()
    await pref.put('lastUser', this.username)

    // 刷新列表
    this.users = await DatabaseHelper.getUsers()
    this.username = ''
  }

  build() {
    Column({ space: 16 }) {
      Text('用户管理')
        .fontSize(24)
        .fontWeight(FontWeight.Bold)

      Row({ space: 12 }) {
        TextInput({ placeholder: '输入用户名', text: this.username })
          .layoutWeight(1)
          .onChange((v) => this.username = v)

        Button('添加')
          .onClick(() => this.addUser())
      }
      .width('100%')
      .padding(16)

      List() {
        ForEach(this.users, (user: User) => {
          ListItem() {
            Row() {
              Column() {
                Text(user.name)
                  .fontSize(16)
                Text(user.email)
                  .fontSize(12)
                  .fontColor('#999')
              }
              .alignItems(HorizontalAlign.Start)

              Button('删除')
                .fontSize(12)
                .onClick(async () => {
                  await DatabaseHelper.deleteUser(user.id)
                  this.users = await DatabaseHelper.getUsers()
                })
            }
            .width('100%')
            .justifyContent(FlexAlign.SpaceBetween)
            .padding(16)
          }
        })
      }
      .width('100%')
      .layoutWeight(1)
    }
    .width('100%')
    .height('100%')
  }
}

最佳实践

  1. 选择合适存储方案:小配置用 Preferences,结构化数据用 RDB,大文件用文件存储
  2. 及时 flush:Preferences 写入后必须调用 flush() 才能持久化
  3. 关闭资源:数据库查询完关闭 ResultSet,文件操作完关闭文件描述符
  4. 使用事务:批量操作使用事务保证数据一致性
  5. 异常处理:文件操作可能失败,使用 try/catch 处理
  6. 数据迁移:应用升级时考虑数据库版本迁移
  7. 加密敏感数据:涉及敏感信息使用 SecurityLevel.S3/S4 加密存储

参考链接