HarmonyOS鸿蒙Next中为什么使用preferences的flush持久化成功的数据在重启应用后还是被清除了

HarmonyOS鸿蒙Next中为什么使用preferences的flush持久化成功的数据在重启应用后还是被清除了 以下是代码

import { preferences } from '@kit.ArkData';
import common from '@ohos.app.ability.common';

class Store {
  private dataPreferences: preferences.Preferences | null = null;
  private context: common.UIAbilityContext | null = null;

  /** 初始化 */
  init(context: common.UIAbilityContext) {
    this.context = context
    preferences.getPreferences(context, 'myStore', (err: BusinessError, obj: preferences.Preferences) => {
      if (err === undefined) {
        console.log('初始化成功')
        this.dataPreferences = obj
      } else {
        this.dataPreferences = null
        console.log('失败原因:', err.message)
      }
    });
  }

  /** 设置token */
  setToken(token: string) {
    console.log('设置token', token);
    this.dataPreferences?.put('token', token, (err: BusinessError) => {
      if (err === undefined) {
        console.log('写入成功')
        this.flushToken()
      } else {
        this.setToken(token)
      }
    })

  }

  /** 持久化token */
  flushToken() {
    this.dataPreferences?.flush((flushErr: BusinessError) => {
      if (flushErr === undefined) {
        console.log('持久化成功')
      } else {
        console.log('持久化失败:', flushErr.message)
        this.flushToken()
      }
    })
  }

  /** 获取token */
  getToken(): string {
    try {
      return this.dataPreferences?.getSync('token', '') as string
    } catch (err) {
      return ''
    }

  }

  /** 清除token */
  removeToken() {
    this.dataPreferences?.delete('token', (err: BusinessError) => {
      if (err === undefined) {
        console.log('删除成功')
        this.flushToken()
      } else {
        this.removeToken()
      }
    })
  }
}

export default new Store()

在EntryAbility中初始化,在其它页面使用set和get方法均成功,但是重启应用后数据就没有了,使用的是模拟器(Mate 80 Pro Max)


更多关于HarmonyOS鸿蒙Next中为什么使用preferences的flush持久化成功的数据在重启应用后还是被清除了的实战教程也可以访问 https://www.itying.com/category-93-b0.html

9 回复

伙伴您好,分析问题为使用getPreferences()异步方法引起,可采用如下方式解决:

【解决方案】

getPreferences()方法为异步方法,在后续使用首选项Preferences实例时需先确保初始化完成,改用getPreferencesSync()同步方法初始化首选项实例或在初始化方法前加上await等待首选项初始化完成后,再使用插入、查询、删除等方法。

【背景知识】 用户首选项为应用提供Key-Value键值型的数据处理能力,支持应用持久化轻量级数据,并对其修改和查询。数据存储形式为键值对,键的类型为字符串型,值的存储数据类型包括数字型、字符型、布尔型以及这3种类型的数组类型。详情请参考用户首选项使用指南及相关API

更多关于HarmonyOS鸿蒙Next中为什么使用preferences的flush持久化成功的数据在重启应用后还是被清除了的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


1、检查运行配置,是否勾选 Keep Application Data

cke_420.png

2、直接通过Device File Browser查看沙箱里 preferences 文件, 位置在: /data/storage/el2/base/haps/entry/preferences/preferences

感谢找到原因了,

重启后,在get数据的时候,打印下this.dataPreferences是否存在,有可能这个时候还没初始化成功;

感谢找到原因了,

最近我也遇到这种问题,

我是还没有初始化完成,你可以看一下你的是不是也这样,

在HarmonyOS Next中,使用preferencesflush方法后数据被清除,通常是因为应用重启时未正确加载持久化文件。flush方法仅将数据写入内存并异步保存到文件,若应用在保存完成前异常退出,数据可能丢失。请确保使用getPreferences加载同一文件,并检查文件路径与名称是否一致。此外,确认应用未启用备份恢复机制覆盖数据。

根据你的代码描述,问题很可能出在 getPreferences 的异步初始化getToken 的同步获取 之间的时序竞争上。

你的 Store 类在 EntryAbility 中调用 init 方法初始化。这是一个异步回调操作。在 init 的回调函数执行成功,将 this.dataPreferences 赋值之前,其他页面或组件可能已经同步调用了 getToken() 方法。

此时 this.dataPreferences 仍为 nullgetToken() 方法会直接返回空字符串 '',给你造成了“数据被清除”的假象。实际上,数据可能已经成功持久化到文件中,只是你的应用在启动初期未能正确加载到内存中的 Preferences 对象。

解决方案:

  1. 确保初始化完成后再进行数据操作:这是最根本的解决方法。你需要确保在所有 setTokengetToken 等操作执行前,dataPreferences 对象已经完成初始化。可以通过以下方式实现:

    • 使用 Promise 或 Async/Await 重构初始化:将 getPreferences 封装成返回 Promise 的函数,在 EntryAbilityonCreate 阶段 await 其完成。
    • 添加状态标志:在类中添加一个 initialized 布尔标志,在 init 的回调成功后将其设为 true。在所有数据操作方法(getToken, setToken)开始时检查此标志,如果未初始化,则等待或返回默认值/执行初始化。
  2. 检查 flush 回调:你的代码中 flush 的回调里已经打印了“持久化成功”或失败信息。请务必确认你看到的是“持久化成功”的日志。如果打印了失败信息,需要根据错误信息排查。

  3. 使用同步初始化方法(如果可用):查阅 HarmonyOS Next 的 API 文档,看 @kit.ArkData 中的 preferences 是否提供了同步的 getPreferencesSync 方法。如果有,在 EntryAbility 的同步启动路径中使用它,可以避免此异步问题。

代码修改示例(思路1 - Promise封装):

class Store {
  private dataPreferences: preferences.Preferences | null = null;
  private initPromise: Promise<void> | null = null; // 新增初始化Promise

  init(context: common.UIAbilityContext): Promise<void> { // 返回Promise
    if (this.initPromise) {
      return this.initPromise;
    }
    this.initPromise = new Promise((resolve, reject) => {
      preferences.getPreferences(context, 'myStore', (err: BusinessError, obj: preferences.Preferences) => {
        if (err === undefined) {
          console.log('初始化成功');
          this.dataPreferences = obj;
          resolve();
        } else {
          this.dataPreferences = null;
          console.log('失败原因:', err.message);
          reject(err);
        }
      });
    });
    return this.initPromise;
  }

  async getToken(): Promise<string> { // 改为异步方法
    if (!this.dataPreferences) {
      // 可以选择在这里等待初始化,或者在调用getToken前确保已await init()
      return '';
    }
    try {
      return this.dataPreferences?.getSync('token', '') as string;
    } catch (err) {
      return '';
    }
  }

  // setToken 等方法也应考虑类似的处理,或确保在调用它们之前init已完成。
}

// 在 EntryAbility 中
import store from '../store'; // 你的Store路径

onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
  // 初始化并等待完成
  store.init(this.context).then(() => {
    console.log('Store初始化完毕,可以安全操作数据');
    // 可以在这里进行一些初始数据读取
  }).catch((err) => {
    console.error('Store初始化失败:', err);
  });
}

总结: 核心问题是 异步初始化未完成时进行了同步数据访问。请按照上述思路调整代码,确保数据操作发生在 Preferences 实例准备就绪之后。

回到顶部