在跨平台应用开发中,轻量级本地键值对存储(如 Android 的 SharedPreferences、iOS 的 UserDefaults 以及鸿蒙的 Preferences)是高频使用的基础能力。为了实现“一码多端”,我们需要在 ArkTS 层进行统一封装。

一、 核心适配策略:优先使用 ArkTS 原生 API

ArkUI-X 框架已经对部分鸿蒙原生 API 进行了跨平台适配。对于轻量级本地存储,首选 ArkTS 原生的 @ohos.data.preferences 模块。该模块在 ArkUI-X 中已实现了对 Android 和 iOS 底层存储机制的自动映射,开发者无需手动编写原生桥接代码。

二、 统一存储工具类封装

为了屏蔽底层实例获取、异步读写以及数据类型转换的复杂性,建议封装一个全局统一的 StorageManager 工具类。

核心代码示例(跨平台 Preferences 封装):

import { preferences } from '@kit.ArkTS';

export class StorageManager {
  private static readonly STORE_NAME = 'app_global_store';
  private static context: Context;

  // 初始化存储上下文(通常在 EntryAbility 的 onCreate 中调用)
  static init(context: Context) {
    StorageManager.context = context;
  }

  // 异步写入数据
  static async put(key: string, value: string | number | boolean): Promise<void> {
    try {
      const store = await preferences.getPreferences(StorageManager.context, StorageManager.STORE_NAME);
      await store.put(key, value);
      await store.flush(); // 必须调用 flush 确保数据落盘
    } catch (e) {
      console.error(`Storage put failed for key [${key}]:`, e);
    }
  }

  // 异步读取数据
  static async get(key: string, defaultValue: string | number | boolean): Promise<string | number | boolean> {
    try {
      const store = await preferences.getPreferences(StorageManager.context, StorageManager.STORE_NAME);
      return await store.get(key, defaultValue);
    } catch (e) {
      console.error(`Storage get failed for key [${key}]:`, e);
      return defaultValue;
    }
  }
}

三、 原生侧集成与注意事项

虽然 preferences 模块支持跨平台,但在 Android/iOS 原生侧仍需确保基础环境配置正确:

  1. Android 侧:确保 StageApplication 正确初始化,框架会自动在 Android 端创建对应的 SharedPreferences 文件。
  2. iOS 侧:底层会自动映射为 NSUserDefaults 存储,数据会持久化到 App 的沙盒目录中。
  3. 线程安全preferences 的读写操作本质上是异步 I/O 操作,严禁在主 UI 线程同步阻塞调用。务必使用 await 或将其放入 TaskPool 中执行。

四、 进阶场景:复杂对象与跨平台 Bridge 降级方案

当需要存储复杂的嵌套对象,或遇到特定旧版本 ArkUI-X 对 preferences 跨平台支持不完善时,可采用接口驱动 + Bridge 降级的策略:

  1. 定义统一接口:声明 saveUserConfig(config: UserConfig) 等纯业务接口。
  2. 鸿蒙实现:直接序列化对象后调用 preferences.put
  3. Android/iOS 降级实现:通过前文提到的 Bridge 通道,将 JSON 字符串传递给原生侧,由原生侧调用 SharedPreferences / UserDefaults 进行存储。这种方案能最大程度利用原生平台的成熟生态,同时保持 ArkTS 层代码的整洁。

五、 复杂对象序列化与泛型安全存储

在实际业务中,我们通常需要存储复杂的嵌套对象(如用户信息、配置项)。结合 TypeScript 的泛型特性,可以实现类型安全的对象存取,避免手动进行 JSON 转换。

核心代码示例(泛型对象存储):

export class StorageManager {
  // 泛型写入:自动将对象序列化为 JSON 字符串
  static async putObject<T>(key: string, value: T): Promise<void> {
    try {
      const store = await preferences.getPreferences(StorageManager.context, StorageManager.STORE_NAME);
      await store.put(key, JSON.stringify(value));
      await store.flush();
    } catch (e) {
      console.error(`Storage putObject failed for key [${key}]:`, e);
    }
  }

  // 泛型读取:自动反序列化并还原对象类型
  static async getObject<T>(key: string, defaultValue: T): Promise<T> {
    try {
      const store = await preferences.getPreferences(StorageManager.context, StorageManager.STORE_NAME);
      const jsonStr = await store.get(key, '') as string;
      return jsonStr ? JSON.parse(jsonStr) as T : defaultValue;
    } catch (e) {
      console.error(`Storage getObject failed for key [${key}]:`, e);
      return defaultValue;
    }
  }
}

六、 响应式数据绑定与状态同步

为了让本地存储的变化能够实时驱动 UI 更新,可以结合 ArkUI 的状态管理机制,将存储操作与响应式变量绑定。

核心代码示例(结合 @State 的响应式存储):

@Entry
@Component
struct SettingsPage {
  @State theme: string = 'light';

  async aboutToAppear() {
    // 页面加载时从本地存储读取
    this.theme = await StorageManager.get('app_theme', 'light') as string;
  }

  async toggleTheme() {
    const newTheme = this.theme === 'light' ? 'dark' : 'light';
    this.theme = newTheme; // 直接修改状态变量触发 UI 刷新
    await StorageManager.put('app_theme', newTheme); // 同步持久化到本地
  }

  build() {
    Column() {
      Text(`当前主题: ${this.theme}`)
      Button('切换主题').onClick(() => this.toggleTheme())
    }
  }
}

七、 多配置文件隔离与命名空间管理

当应用功能模块较多时,将所有键值对塞入同一个配置文件会导致数据耦合与读取性能下降。建议按业务模块划分独立的命名空间。

核心代码示例(多实例隔离):

export class StorageManager {
  // 获取指定模块的存储实例
  static async getStore(moduleName: string): Promise<preferences.Preferences> {
    return await preferences.getPreferences(StorageManager.context, moduleName);
  }
}

// 业务调用:用户模块与系统配置模块完全隔离
const userStore = await StorageManager.getStore('user_profile');
await userStore.put('username', 'Alice');

const sysStore = await StorageManager.getStore('sys_config');
await sysStore.put('auto_sync', true);

八、 跨端数据迁移与版本兼容策略

在应用升级过程中,本地存储的 Key 或数据结构可能发生变更。需要建立版本控制与数据迁移机制,防止老版本用户升级后出现数据丢失或崩溃。

核心代码示例(带版本号的初始化与迁移):

export class StorageManager {
  private static readonly DB_VERSION_KEY = 'app_db_version';
  private static readonly CURRENT_VERSION = 2;

  static async initAndMigrate(context: Context) {
    StorageManager.context = context;
    const store = await preferences.getPreferences(context, StorageManager.STORE_NAME);
    const oldVersion = await store.get(StorageManager.DB_VERSION_KEY, 1) as number;

    if (oldVersion < StorageManager.CURRENT_VERSION) {
      // 执行数据迁移逻辑
      console.info(`Migrating storage from v${oldVersion} to v${StorageManager.CURRENT_VERSION}`);
      // 例如:将旧 key 'user_token' 迁移到新 key 'auth_token'
      const oldToken = await store.get('user_token', '') as string;
      if (oldToken) {
        await store.put('auth_token', oldToken);
        await store.delete('user_token');
      }
      
      // 更新版本号
      await store.put(StorageManager.DB_VERSION_KEY, StorageManager.CURRENT_VERSION);
      await store.flush();
    }
  }
}
Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐