本地存储适配:SharedPreferences与UserDefaults的统一封装(110)
在跨平台应用开发中,轻量级本地键值对存储(如 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 原生侧仍需确保基础环境配置正确:
- Android 侧:确保
StageApplication正确初始化,框架会自动在 Android 端创建对应的SharedPreferences文件。 - iOS 侧:底层会自动映射为
NSUserDefaults存储,数据会持久化到 App 的沙盒目录中。 - 线程安全:
preferences的读写操作本质上是异步 I/O 操作,严禁在主 UI 线程同步阻塞调用。务必使用await或将其放入TaskPool中执行。
四、 进阶场景:复杂对象与跨平台 Bridge 降级方案
当需要存储复杂的嵌套对象,或遇到特定旧版本 ArkUI-X 对 preferences 跨平台支持不完善时,可采用接口驱动 + Bridge 降级的策略:
- 定义统一接口:声明
saveUserConfig(config: UserConfig)等纯业务接口。 - 鸿蒙实现:直接序列化对象后调用
preferences.put。 - 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();
}
}
}更多推荐




所有评论(0)