鸿蒙应用如何实现内存级别全局缓存数据?

鸿蒙应用如何实现内存级别全局缓存数据?

一、结论:

该问题首先需要搞清楚,全局缓存的数据,是内存级别缓存,即数据只存在App运行期间。还是持久化级别,即数据在App关闭再重新打开,依旧可以获取。

1、首先内存级别:

可以使用(1)AppStorage(2)单例管理(3)LRUCache管理。

2、其次持久化级别:

(1)PersistentStorage(2)Preferences 首选项(3)以及关系型数据库。

二、代码实现和详细解释:

1、首先内存级别:
(1)AppStorage

typescript 复制代码
// 存储数据
AppStorage.SetOrCreate<string>('userToken', 'abc123');

// 获取数据
let token: string = AppStorage.Get<string>('userToken');

// 组件内使用(自动绑定)
@Entry
@Component
struct UserInfo {
  @StorageLink('userToken') token: string = 'test';

  build() {
    Text(`Token: ${this.token}`)
  }
}

(2)单例管理

GlobalCacheMgr 示例:

typescript 复制代码
export class GlobalCacheMgr {
  // 移除多余空格,规范类型声明
  private static mGlobalCacheMgr: GlobalCacheMgr;
  private cacheMap: Map<string, Object> = new Map();

  public static Ins(): GlobalCacheMgr {
    if (!GlobalCacheMgr.mGlobalCacheMgr) {
      GlobalCacheMgr.mGlobalCacheMgr = new GlobalCacheMgr();
    }
    return GlobalCacheMgr.mGlobalCacheMgr;
  }
  
  // 存储数据
  set(key: string, value: Object): void {
    this.cacheMap.set(key, value);
  }

  // 获取数据:泛型推导更安全,避免强制类型断言的风险
  get<T>(key: string): T | undefined {
    const value = this.cacheMap.get(key);
    return value as T | undefined;
  }

  // 增加缓存清理方法,避免内存泄漏
  clear(key?: string): void {
    if (key) {
      this.cacheMap.delete(key);
    } else {
      this.cacheMap.clear();
    }
  }
  
}

调用示例:

typescript 复制代码
import { GlobalCacheMgr } from '../mgr/GlobalCacheMgr';

/**
 * 全局缓存单例管理类 测试页面
 */
@Entry
@Component
struct GlobalCacheMgrTestPage {
  private TAG: string = "ContactPage";

  private onClickTest = () => {
    let key: string = "userToken";
    GlobalCacheMgr.Ins().set(key, "test");
    let result: string | undefined = GlobalCacheMgr.Ins().get(key);
    console.log(this.TAG, " onClickTest result: " + result);
  }

  build() {
    Row() {
      Button('点击测试全局缓存')
        .onClick(this.onClickTest)
    }
    .justifyContent(FlexAlign.Center)
    .size({
      width: "100%",
      height: "100%"
    })
  }
}

(3)LRUCache管理

LRUCache在图片缓存中使用最多,采用链表的方式,设置最大值,每次填入数据,通过key去判断,如果有,且没有达到最大值,则更新。如果达到最大值,则删除最头部的数据。保证高频数据的缓存,减轻内存的压力。

LRUCacheMgr 管理类示例:

typescript 复制代码
import { util } from '@kit.ArkTS';

/**
 * LRU缓存工具类(单例模式)
 * 基于HarmonyOS内置util.LRUCache实现,采用LRU(最近最少使用)淘汰策略
 * 当缓存数量达到容量上限时,自动移除最久未使用的缓存项
 */
export class LRUCacheMgr {
  // 单例实例(全局唯一)
  private static instance: LRUCacheMgr;
  // LRU缓存核心实例:键为string类型,值为任意Object类型
  private lruCache: util.LRUCache<string, Object>;
  // 初始最大缓存容量(测试用,默认5个缓存项)
  private MAX_NUM: number = 5;

  /**
   * 私有构造函数
   * 禁止外部通过new关键字实例化,保证单例特性
   * 初始化LRUCache并设置初始最大容量
   */
  private constructor() {
    this.lruCache = new util.LRUCache(this.MAX_NUM);
  }

  /**
   * LRUCacheMgr(懒汉式初始化)
   * 首次调用时创建实例,后续调用返回已创建的实例
   * @returns LRUCacheMgr 全局唯一的单例对象
   */
  public static getInstance(): LRUCacheMgr {
    if (!LRUCacheMgr.instance) {
      LRUCacheMgr.instance = new LRUCacheMgr();
    }
    return LRUCacheMgr.instance;
  }

  /**
   * 判断LRU缓存是否为空
   * @returns boolean - true:缓存为空;false:缓存非空
   */
  public isEmpty(): boolean {
    return this.lruCache.isEmpty();
  }

  /**
   * 获取LRU缓存的当前最大容量(可存储的缓存项数量上限)
   * @returns number 缓存容量值
   */
  public getCapacity(): number {
    return this.lruCache.getCapacity();
  }

  /**
   * 重置/更新LRU缓存的最大容量
   * @param newCapacity 新的缓存容量(需传入大于0的数字)
   */
  public updateCapacity(newCapacity: number) {
    this.lruCache.updateCapacity(newCapacity);
  }

  /**
   * 向LRU缓存中添加/更新缓存项
   * 若key已存在,会覆盖原有值;若容量已满,会淘汰最久未使用的缓存项
   * @param key 缓存键(唯一标识,字符串类型)
   * @param value 缓存值(任意Object类型,如字符串、对象、数字等)
   */
  public putCache(key: string, value: Object) {
    this.lruCache.put(key, value);
  }

  /**
   * 删除指定key对应的缓存项
   * 若key不存在,该操作无效果
   * @param key 要删除的缓存键
   */
  public remove(key: string) {
    this.lruCache.remove(key);
  }

  /**
   * 获取指定key对应的缓存值
   * @param key 缓存键
   * @returns Object | undefined - 存在则返回缓存值,不存在则返回undefined
   */
  public getCache(key: string): Object | undefined {
    return this.lruCache.get(key);
  }

  /**
   * 判断缓存中是否包含指定key的缓存项
   * @param key 缓存键
   * @returns boolean - true:存在;false:不存在
   */
  public contains(key: string): boolean {
    return this.lruCache.contains(key);
  }

  /**
   * 清空所有缓存数据,并将缓存容量重置为64
   */
  public clearCache() {
    this.lruCache.clear(); // 清空缓存内容
    this.lruCache.updateCapacity(64); // 重置缓存容量
  }
}

调用示例:

typescript 复制代码
import { LRUCacheMgr } from '../mgr/LRUCacheMgr';


export class TestInfo{
  id: number = 0;
  name: string = "";
  age: number = 0;
}
/**
 * LRU缓存工具类 测试页面
 */
@Entry
@Component
struct LRUCacheMgrTestPage {
  // 日志TAG,便于定位日志来源
  private TAG: string = "LRUCacheMgrTestPage";

  /**
   * 点击按钮触发LRU缓存全流程测试
   * 依次测试:初始化、添加缓存、获取缓存、判断存在性、更新容量、删除缓存、清空缓存等
   */
  private onClickTestLRU = () => {
    // 1. 获取LRU缓存单例实例
    const lruCache = LRUCacheMgr.Ins();
    console.log(this.TAG, "===== 开始测试LRU缓存 =====");

    // 2. 初始状态检查
    const isEmptyInit = lruCache.isEmpty();
    const initCapacity = lruCache.getCapacity();
    console.log(this.TAG, `初始状态 - 缓存是否为空:${isEmptyInit},初始容量:${initCapacity}`);

    // 3. 添加不同类型的缓存项
    const tokenKey = "userToken";
    const userInfoKey = "userInfo";
    const maxCountKey = "maxCount";
    lruCache.putCache(tokenKey, "hmos_987654321");
    let testInfo: TestInfo = { id: 1001, name: "鸿蒙开发者", age: 25 };
    lruCache.putCache(userInfoKey, testInfo);
    lruCache.putCache(maxCountKey, 200);
    console.log(this.TAG, "添加缓存项 - userToken/userInfo/maxCount");

    // 4. 获取缓存项并打印
    const token = lruCache.getCache(tokenKey);
    const userInfo = lruCache.getCache(userInfoKey);
    const maxCount = lruCache.getCache(maxCountKey);
    console.log(this.TAG, `获取缓存 - ${tokenKey}: ${token}`);
    console.log(this.TAG, `获取缓存 - ${userInfoKey}: ${JSON.stringify(userInfo)}`);
    console.log(this.TAG, `获取缓存 - ${maxCountKey}: ${maxCount}`);

    // 5. 判断缓存项是否存在
    const hasUserInfo = lruCache.contains(userInfoKey);
    const hasInvalidKey = lruCache.contains("invalidKey");
    console.log(this.TAG, `判断存在性 - ${userInfoKey}存在:${hasUserInfo},invalidKey存在:${hasInvalidKey}`);

    // 6. 更新缓存容量
    const newCapacity = 10;
    lruCache.updateCapacity(newCapacity);
    const updatedCapacity = lruCache.getCapacity();
    console.log(this.TAG, `更新容量 - 原容量${initCapacity} → 新容量${updatedCapacity}`);

    // 7. 删除指定缓存项
    lruCache.remove(maxCountKey);
    const deletedMaxCount = lruCache.getCache(maxCountKey);
    console.log(this.TAG, `删除缓存 - ${maxCountKey}:${deletedMaxCount === undefined ? "删除成功" : "删除失败"}`);

    // 8. 清空缓存并重置容量
    lruCache.clearCache();
    const isEmptyAfterClear = lruCache.isEmpty();
    const finalCapacity = lruCache.getCapacity();
    console.log(this.TAG, `清空缓存 - 缓存是否为空:${isEmptyAfterClear},重置后容量:${finalCapacity}`);

    console.log(this.TAG, "===== LRU缓存测试结束 =====");
  }

  build() {
    Row() {
      Button('点击测试LRU缓存')
        .fontSize(16)
        .padding({ left: 20, right: 20, top: 10, bottom: 10 })
        .onClick(this.onClickTestLRU)
    }
    .justifyContent(FlexAlign.Center)
    .size({
      width: "100%",
      height: "100%"
    })
  }
}

资料引用:

AppStorage 应用级变量的内存管理: https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-state-management#appstorage

LRUCache 应用内存占用优化: https://developer.huawei.com/consumer/cn/doc/best-practices/bpta-memory-optimization#section1518265464211

Preferences 用户首选项:

https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-data-preferences#preferencesgetpreferences

相关推荐
二流小码农15 分钟前
鸿蒙开发:一个底部的曲线导航
android·ios·harmonyos
特立独行的猫a27 分钟前
OpenHarmony开源鸿蒙应用签名机制深度解析与工具使用指南
华为·开源·harmonyos·签名
Fate_I_C35 分钟前
Flutter鸿蒙0-1开发-flutter create <prjn>
flutter·华为·harmonyos·鸿蒙
Python私教39 分钟前
鸿蒙应用的网络请求和数据处理:从HTTP到本地缓存的完整方案
网络·http·harmonyos
Bigger9 小时前
Flutter 开发实战:解决华为 HarmonyOS 任务列表不显示 App 名称的终极指南
android·flutter·华为
梧桐ty13 小时前
鸿蒙应用冷启动优化:Flutter首屏秒开与白屏治理实战
flutter·华为·harmonyos
梧桐ty14 小时前
驾驭未来:基于鸿蒙的Flutter车载应用与手机端协同实战
flutter·华为·harmonyos
FrameNotWork15 小时前
HarmonyOS 教学实战(五):路由、页面生命周期与多页面架构
华为·架构·harmonyos
云和数据.ChenGuang16 小时前
鸿蒙电视的核心技术
华为·harmonyos·数据库运维工程师·运维教程
AirDroid_cn17 小时前
鸿蒙NEXT:升级系统时提示 “存储空间不足” 如何解决?
华为·harmonyos