一、前言

在 OpenHarmony 应用开发中,Preferences 偏好存储常用于保存轻量键值对数据:用户配置、登录状态、开关设置、缓存标识等,数据持久保存在本地,应用重启、设备关机后数据不丢失。相较于数据库,Preferences 使用简单、专注字符串 / 数字 / 布尔等基础类型,是项目高频本地存储方案。本篇完整讲解 API 用法、存取、删除、同步 / 异步操作,搭配落地代码。

依赖模块:@ohos.data.preferences

二、核心基础概念

  1. Preferences 实例:以文件名区分数据库,一个文件对应一套独立键值数据;
  2. 存储数据类型:支持string、number、boolean、Array<string>四种;
  3. 数据落盘:写入数据后必须调用flush()才会持久化写入磁盘,否则只在内存。

三、前置导入

ets

import preferences from '@ohos.data.preferences'
import common from '@ohos.app.ability.common'

获取上下文 Context(Ability 上下文,存储必备参数)。

四、基础 CRUD 案例

4.1 封装获取 Preferences 工具对象

ets

// 获取偏好存储实例
async function getPrefer(context: common.UIAbilityContext, fileName: string) {
  let prefer = await preferences.getPreferences(context, fileName)
  return prefer
}

4.2 写入 & 保存数据

ets

@Entry
@Component
struct PreWriteDemo {
  // 上下文
  context = getContext(this) as common.UIAbilityContext

  async saveData() {
    // 打开userinfo.xml存储文件
    let prefer = await getPrefer(this.context, "userinfo")
    // 存入多组键值
    prefer.putSync("username", "Harmony开发者")
    prefer.putSync("age", 22)
    prefer.putSync("isLogin", true)
    prefer.putSync("hobby", ["编程", "嵌入式", "鸿蒙开发"])
    // 关键:持久化落地磁盘
    await prefer.flush()
    console.info("数据保存成功")
  }

  build() {
    Column() {
      Button("保存用户信息")
        .width(200)
        .onClick(() => this.saveData())
    }
    .width("100%")
    .height("100%")
    .justifyContent(FlexAlign.Center)
  }
}

4.3 读取本地存储数据

ets

@Entry
@Component
struct PreReadDemo {
  @State name: string = ""
  @State age: number = 0
  @State loginStat: boolean = false
  context = getContext(this) as common.UIAbilityContext

  async readData() {
    let prefer = await getPrefer(this.context, "userinfo")
    // 参数:键名、默认值(无数据时返回默认)
    this.name = prefer.getSync("username", "未知用户")
    this.age = prefer.getSync("age", 0)
    this.loginStat = prefer.getSync("isLogin", false)
  }

  build() {
    Column({ space: 15 }) {
      Button("读取存储数据").onClick(() => this.readData())
      Text(`用户名:${this.name}`).fontSize(18)
      Text(`年龄:${this.age}`).fontSize(18)
      Text(`登录状态:${this.loginStat ? "已登录" : "未登录"}`).fontSize(18)
    }
    .padding(30)
    .width("100%")
    .height("100%")
    .justifyContent(FlexAlign.Center)
  }
}

4.4 删除指定键、清空全部数据

ets

@Entry
@Component
struct PreDelDemo {
  context = getContext(this) as common.UIAbilityContext

  // 删除单个key
  async delKey() {
    let prefer = await getPrefer(this.context, "userinfo")
    prefer.deleteSync("age")
    await prefer.flush()
    console.info("age字段删除完成")
  }

  // 清空当前文件所有数据
  async clearAll() {
    let prefer = await getPrefer(this.context, "userinfo")
    prefer.clearSync()
    await prefer.flush()
    console.info("全部数据清空")
  }

  build() {
    Column({ space:20 }) {
      Button("删除age字段").onClick(()=>this.delKey())
      Button("清空全部数据").onClick(()=>this.clearAll())
    }
    .width("100%")
    .height("100%")
    .justifyContent(FlexAlign.Center)
  }
}

五、异步写法(Promise 方式,项目主流)

除了putSync/getSync同步 API,官方推荐异步 API 避免阻塞 UI 线程:

ets

async function asyncOpt(context: common.UIAbilityContext) {
  let prefer = await preferences.getPreferences(context, "setting")
  // 异步存
  await prefer.put("nightMode", true)
  // 异步读
  let res = await prefer.get("nightMode", false)
  console.info("夜间模式:" + res)
  await prefer.flush()
}

六、综合实战:应用设置页持久化开关

结合之前 Toggle 开关组件,实现开关状态永久保存,重启 APP 状态不变

ets

import preferences from '@ohos.data.preferences'
import common from '@ohos.app.ability.common'

@Component
struct getPreferTool {
  static async getPre(context: common.UIAbilityContext, name: string) {
    return await preferences.getPreferences(context, name)
  }
}

@Entry
@Component
struct SettingStorePage {
  @State nightOpen: boolean = false
  @State notifyOpen: boolean = false
  context = getContext(this) as common.UIAbilityContext

  aboutToAppear() {
    // 页面加载读取本地存储
    this.loadSetting()
  }

  // 读取配置
  async loadSetting() {
    let pre = await getPreferTool.getPre(this.context, "app_setting")
    this.nightOpen = await pre.get("nightMode", false)
    this.notifyOpen = await pre.get("notify", true)
  }

  // 保存配置
  async saveSetting() {
    let pre = await getPreferTool.getPre(this.context, "app_setting")
    await pre.put("nightMode", this.nightOpen)
    await pre.put("notify", this.notifyOpen)
    await pre.flush()
  }

  build() {
    Column({ space: 20 }) {
      Text("系统偏好设置").fontSize(22).fontWeight(FontWeight.Bold).margin({bottom:20})

      Row() {
        Text("夜间模式").fontSize(18).layoutWeight(1)
        Toggle({ isOn: this.nightOpen })
          .onChange((v: boolean) => {
            this.nightOpen = v
            this.saveSetting()
          })
      }
      .width("100%")
      .padding(15)
      .backgroundColor("#fff")
      .borderRadius(8)

      Row() {
        Text("消息推送").fontSize(18).layoutWeight(1)
        Toggle({ isOn: this.notifyOpen })
          .onChange((v: boolean) => {
            this.notifyOpen = v
            this.saveSetting()
          })
      }
      .width("100%")
      .padding(15)
      .backgroundColor("#fff")
      .borderRadius(8)
    }
    .width("100%")
    .height("100%")
    .padding(20)
    .backgroundColor("#f5f5f5")
  }
}

效果:修改开关后自动落地本地,关闭应用再次打开,开关状态和上次一致。

七、开发使用规范与注意事项

  1. 适用场景
  • ✅ 小型配置、登录标记、开关状态、用户简单信息
  • ❌ 大批量数据、复杂结构化数据(改用关系型数据库 RDB)
  1. flush () 必写:所有修改操作后必须执行 flush,否则数据仅存在内存,卸载 / 重启丢失;
  2. 文件名规范:按业务拆分文件,用户数据 user.xml、配置 setting.xml,不要全部塞进同一个文件;
  3. 默认值:get 时必须设置兜底默认值,防止 key 不存在返回 undefined 导致页面报错。
Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐