鸿蒙Cordova开发踩坑记录:LocalStorage的“失忆症“

摘要:LocalStorage 是 Web 前端最常用的轻量级存储。但在鸿蒙混合开发中,我们遭遇了一次严重的"数据丢失"事故:应用更新或重新签名打包后,用户的游戏最高分和设置项全部清零。本文揭示了 ArkWeb 数据存储的目录机制,并给出了基于 Native Preferences 的持久化替代方案。

😱 1. 数据去哪了?

事故复现

  1. 用户安装 v1.0 版本,玩到了 20480 分。
  2. 我们发布 v1.1 版本(可能更换了签名证书或 Bundle Name 微调)。
  3. 用户覆盖安装。
  4. 打开游戏,最高分变成 0,用户炸毛。

原因深究
Android WebView 和鸿蒙 ArkWeb 的 LocalStorage 数据是存储在应用沙箱下的特定目录中的。
路径通常包含 Bundle NameWebview ID
如果应用的签名变更,或者系统认为这是两个不同的应用实例,系统出于安全考虑,可能会重建沙箱,导致旧的 app_webview 目录被清空或无法访问。

对于 Web App 来说,LocalStorage 被设计为"浏览器缓存"的一部分,它并不像文件系统那样具备"持久化承诺"。系统空间不足时,甚至可能自动清理。

🛡️ 2. 救援行动:Native 存储

为了保证数据绝对安全,关键数据(如用户存档、Token、设置)绝不能只存放在 LocalStorage。
必须存储在 Native 层的 Preferences (首选项)Database (数据库) 中。

2.1 构建 NativeStorage 插件

我们封装一个 NativeStorage 类,通过 JSBridge 暴露给前端。

ArkTS 实现

import dataPreferences from '@ohos.data.preferences';

class NativeStorageProxy {
    private prefs: dataPreferences.Preferences;

    async init(context) {
        // getPreferences 是持久化的,除非卸载应用,否则数据一直存在
        this.prefs = await dataPreferences.getPreferences(context, 'game_data');
    }

    save(key: string, value: string) {
        this.prefs.put(key, value).then(() => {
            this.prefs.flush(); // 关键:立即写入磁盘
        });
    }

    async get(key: string) {
        return await this.prefs.get(key, '');
    }
}

2.2 前端无感替换

为了不修改大量业务代码,我们在前端劫持 localStorage 对象(或者封装一个兼容层)。

const StorageAdapter = {
    setItem: async (key, value) => {
        // 双写策略:既存 LocalStorage (快),也存 Native (稳)
        localStorage.setItem(key, value);
        if (window.gameNative) {
            window.gameNative.saveData(key, value);
        }
    },
    
    getItem: async (key) => {
        // 优先读 LocalStorage(同步,速度快)
        let val = localStorage.getItem(key);
        if (!val && window.gameNative) {
            // 如果 LocalStorage 丢了,尝试从 Native 捞回
            val = await window.gameNative.getData(key);
            if (val) {
                // 修复 LocalStorage
                localStorage.setItem(key, val); 
            }
        }
        return val;
    }
};

🔄 3. 数据迁移策略

应用启动时的**“数据同步仪式”**:

index.html 加载完成后:

  1. 检查 localStorage 是否为空。
  2. 如果为空,调用 NativeStorage.getAll()
  3. 将 Native 数据全量灌入 localStorage
  4. 游戏开始。

这样,业务逻辑依然可以同步读取 localStorage,无需把所有代码都改成 await 异步模式,既保留了开发便利性,又保证了数据安全性。

⚠️ 4. 避坑指南

  1. 不要存大对象:Preferences 适合存 Key-Value 小数据。如果你要存几兆的 JSON,请改用文件存储(参考第 8 篇博文)。
  2. 签名必须一致:即使是 Native 存储,如果应用签名变了,鸿蒙系统依然会视为新应用,沙箱数据依然会隔离。这是系统级安全机制,无解(除非使用云同步)。
  3. Flush 的时机preferences.flush() 是耗时 IO 操作,不要在每次按键时都调用。建议使用防抖(Debounce)策略,或者在 onBackground 时统一保存。

📚 5. 总结

Web 的 LocalStorage 就像便利贴,方便但易丢。
Native 的 Preferences 就像保险箱,繁琐但安全。
“便利贴 + 保险箱” 的组合方案,是混合应用数据存储的最佳实践。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

淼学派对

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值