HarmonyOS 新手入门:ArkData 用户首选项,无需动辄使用数据库

举报
蓝瘦的蜕变 发表于 2026/06/23 14:19:25 2026/06/23
【摘要】 做 HarmonyOS 数据持久化时,新手很容易先想到数据库。 但不是所有数据都值得建表。比如:昵称、深色模式、字号、是否第一次打开应用,这些都是“小配置”。这种场景用 ArkData 里的用户首选项 Preferences 就够了。 官方文档可以放在手边: 应用数据持久化概述 通过用户首选项实现数据持久化 先说结论

做 HarmonyOS 数据持久化时,新手很容易先想到数据库。

但不是所有数据都值得建表。比如:昵称、深色模式、字号、是否第一次打开应用,这些都是“小配置”。这种场景用 ArkData 里的用户首选项 Preferences 就够了。

官方文档可以放在手边:

先说结论

Preferences 可以理解成应用自己的本地 key-value 文件。

适合存这些:

  • 用户昵称
  • 深色模式开关
  • 字号大小
  • 排序方式
  • 是否展示过引导页

不适合存这些:

  • 商品列表
  • 聊天记录
  • 多表关系数据
  • 需要复杂查询的数据

一句话:少量配置用 Preferences,复杂业务数据再考虑数据库。

它怎么用

核心就三步。

第一步,拿到首选项文件:

const store = await preferences.getPreferences(context, {
  name: 'user_setting_demo'
});

第二步,写入数据:

await store.put('nickname', 'Harmony 新手');
await store.put('darkMode', true);

第三步,保存到本地文件:

await store.flush();

这里有个小坑:put 只是把数据写到 Preferences 实例里,真正落盘要靠 flush。很多人第一次写 Demo 没保存成功,问题就出在漏了这一步。

读取也很简单:

const nickname = store.getSync('nickname', 'Harmony 新手') as string;

第二个参数是默认值。如果本地还没有这个 key,就返回默认值。

这个 Demo 做什么

我写了一个很小的设置页,功能只有三个:

  • 输入昵称
  • 打开或关闭深色模式
  • 调整字号

点击“保存”后写入 Preferences。下次进入页面时,再把这几个值读出来。

这个 Demo 不追求复杂封装,目的就是让新手先把流程跑通:获取实例、读取、写入、删除、落盘。

关键代码

保存时:

await store.put(KEY_NICKNAME, name);
await store.put(KEY_DARK_MODE, this.darkMode);
await store.put(KEY_FONT_SIZE, this.fontSize);
await store.flush();

读取时:

this.nickname = store.getSync(KEY_NICKNAME, 'Harmony 新手') as string;
this.darkMode = store.getSync(KEY_DARK_MODE, false) as boolean;
this.fontSize = store.getSync(KEY_FONT_SIZE, 18) as number;

删除时:

await store.delete(KEY_NICKNAME);
await store.delete(KEY_DARK_MODE);
await store.delete(KEY_FONT_SIZE);
await store.flush();

你会发现,读写逻辑并不复杂。真正要记住的是:修改完数据后,别忘了 flush()

评论区聊聊

你们平时会把哪些数据放到 Preferences 里?

是主题模式、用户偏好这类配置,还是也会存一些临时业务状态?如果你踩过 flush()、默认值、类型转换这类坑,也可以在评论区补充一下。后面我写 ArkData 其他能力时,可以顺手把这些真实场景带进去。

完整示例代码

ArkDataPreferencesDemo.ets

import { common } from '@kit.AbilityKit';
import { preferences } from '@kit.ArkData';
import { promptAction, router } from '@kit.ArkUI';

const STORE_NAME: string = 'user_setting_demo';
const KEY_NICKNAME: string = 'nickname';
const KEY_DARK_MODE: string = 'darkMode';
const KEY_FONT_SIZE: string = 'fontSize';

@Entry
@Component
struct ArkDataPreferencesDemo {
  @State nickname: string = 'Harmony 新手';
  @State darkMode: boolean = false;
  @State fontSize: number = 18;
  @State tips: string = '启动后自动读取本地首选项';

  private store: preferences.Preferences | undefined = undefined;

  aboutToAppear(): void {
    this.loadSettings();
  }

  private async getStore(): Promise<preferences.Preferences> {
    if (this.store !== undefined) {
      return this.store;
    }

    const context = getContext(this) as common.UIAbilityContext;
    this.store = await preferences.getPreferences(context, { name: STORE_NAME });
    return this.store;
  }

  private async loadSettings(): Promise<void> {
    try {
      const store = await this.getStore();
      this.nickname = store.getSync(KEY_NICKNAME, 'Harmony 新手') as string;
      this.darkMode = store.getSync(KEY_DARK_MODE, false) as boolean;
      this.fontSize = store.getSync(KEY_FONT_SIZE, 18) as number;
      this.tips = '读取成功:重启应用后仍会保留';
    } catch (err) {
      this.tips = '读取失败,请查看日志';
      console.error('Preferences load failed');
    }
  }

  private async saveSettings(): Promise<void> {
    try {
      const store = await this.getStore();
      const name = this.nickname.length > 0 ? this.nickname : 'Harmony 新手';
      await store.put(KEY_NICKNAME, name);
      await store.put(KEY_DARK_MODE, this.darkMode);
      await store.put(KEY_FONT_SIZE, this.fontSize);
      await store.flush();
      this.nickname = name;
      this.tips = '保存成功:put 后调用 flush 才会落盘';
      promptAction.showToast({ message: '保存成功' });
    } catch (err) {
      this.tips = '保存失败,请查看日志';
      promptAction.showToast({ message: '保存失败' });
      console.error('Preferences save failed');
    }
  }

  private async resetSettings(): Promise<void> {
    try {
      const store = await this.getStore();
      await store.delete(KEY_NICKNAME);
      await store.delete(KEY_DARK_MODE);
      await store.delete(KEY_FONT_SIZE);
      await store.flush();
      this.nickname = 'Harmony 新手';
      this.darkMode = false;
      this.fontSize = 18;
      this.tips = '已清空本页保存的首选项';
    } catch (err) {
      this.tips = '清空失败,请查看日志';
      console.error('Preferences reset failed');
    }
  }

  build() {
    Scroll() {
      Column({ space: 18 }) {
        Row() {
          Text('返回')
            .fontSize(14)
            .fontColor('#0EA5E9')
            .onClick(() => {
              router.back();
            })

          Blank()
        }
        .width('100%')

        Text('ArkData Preferences')
          .width('100%')
          .fontSize(28)
          .fontWeight(700)
          .fontColor(this.darkMode ? '#F6F7FB' : '#182431')

        Text('适合保存昵称、开关、字号这类轻量配置。')
          .width('100%')
          .fontSize(14)
          .fontColor(this.darkMode ? '#AEB6C8' : '#56616F')

        Column({ space: 12 }) {
          Text('昵称')
            .fontSize(14)
            .fontColor(this.darkMode ? '#DCE3F2' : '#334155')

          TextInput({ placeholder: '输入昵称', text: this.nickname })
            .height(44)
            .fontSize(16)
            .backgroundColor(this.darkMode ? '#202A3A' : '#F1F5F9')
            .fontColor(this.darkMode ? '#F8FAFC' : '#0F172A')
            .onChange((value: string) => {
              this.nickname = value;
            })
        }
        .width('100%')

        Row() {
          Column({ space: 4 }) {
            Text('深色模式')
              .fontSize(16)
              .fontColor(this.darkMode ? '#F8FAFC' : '#0F172A')
            Text(this.darkMode ? '已开启' : '已关闭')
              .fontSize(12)
              .fontColor(this.darkMode ? '#AEB6C8' : '#64748B')
          }
          .alignItems(HorizontalAlign.Start)
          .layoutWeight(1)

          Toggle({ type: ToggleType.Switch, isOn: this.darkMode })
            .onChange((isOn: boolean) => {
              this.darkMode = isOn;
            })
        }
        .width('100%')

        Column({ space: 10 }) {
          Row() {
            Text('字号')
              .fontSize(16)
              .fontColor(this.darkMode ? '#F8FAFC' : '#0F172A')
              .layoutWeight(1)
            Text(`${this.fontSize}`)
              .fontSize(16)
              .fontWeight(700)
              .fontColor('#0EA5E9')
          }
          .width('100%')

          Slider({ value: this.fontSize, min: 14, max: 28, step: 1 })
            .onChange((value: number) => {
              this.fontSize = value;
            })
        }
        .width('100%')

        Text(`你好,${this.nickname}`)
          .width('100%')
          .padding(16)
          .fontSize(this.fontSize)
          .fontColor(this.darkMode ? '#F8FAFC' : '#0F172A')
          .backgroundColor(this.darkMode ? '#202A3A' : '#E0F2FE')
          .borderRadius(8)

        Row({ space: 12 }) {
          Button('保存')
            .layoutWeight(1)
            .height(44)
            .fontSize(16)
            .backgroundColor('#0EA5E9')
            .onClick(() => {
              this.saveSettings();
            })

          Button('清空')
            .layoutWeight(1)
            .height(44)
            .fontSize(16)
            .fontColor(this.darkMode ? '#F8FAFC' : '#0F172A')
            .backgroundColor(this.darkMode ? '#334155' : '#CBD5E1')
            .onClick(() => {
              this.resetSettings();
            })
        }
        .width('100%')

        Text(this.tips)
          .width('100%')
          .fontSize(13)
          .fontColor(this.darkMode ? '#AEB6C8' : '#64748B')
      }
      .width('100%')
      .padding(24)
    }
    .width('100%')
    .height('100%')
    .backgroundColor(this.darkMode ? '#111827' : '#FFFFFF')
  }
}
【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。