实现一个支持三端的 SharedPreferences 插件

前言

本地存储是移动应用的基础功能,用于保存用户设置、登录状态、缓存数据等。CleanMark AI 使用本地存储保存:

  • 用户登录 Token
  • 引导页是否已显示
  • 用户信息缓存
  • 应用配置

这篇文章我会带你实现一个完整的本地存储插件,支持 Android、iOS、HarmonyOS 三端。


一、本地存储方案对比

1.1 三端原生方案

平台 原生方案 特点
Android SharedPreferences XML 文件存储,键值对
iOS UserDefaults plist 文件存储,键值对
HarmonyOS Preferences 数据库存储,键值对

共同特点:

  • 都是键值对存储
  • 支持基本数据类型(String、int、bool、double)
  • 异步读写
  • 自动持久化

1.2 数据类型支持

Dart 类型 Android iOS HarmonyOS
String
int
double
bool
List<String>

二、插件架构设计

2.1 分层架构

shared_preferences/                    # 应用层
  └─ 依赖 shared_preferences_platform_interface

shared_preferences_platform_interface/ # 接口层
  └─ 定义抽象接口

shared_preferences_android/            # Android 实现
shared_preferences_foundation/         # iOS/macOS 实现
shared_preferences_ohos/               # HarmonyOS 实现

2.2 接口层设计

// packages/shared_preferences_platform_interface/lib/shared_preferences_platform_interface.dart

import 'dart:async';
import 'package:plugin_platform_interface/plugin_platform_interface.dart';

/// SharedPreferences 平台接口
abstract class SharedPreferencesStorePlatform extends PlatformInterface {
  SharedPreferencesStorePlatform() : super(token: _token);

  static final Object _token = Object();
  static SharedPreferencesStorePlatform _instance = MethodChannelSharedPreferencesStore();

  static SharedPreferencesStorePlatform get instance => _instance;

  static set instance(SharedPreferencesStorePlatform instance) {
    PlatformInterface.verifyToken(instance, _token);
    _instance = instance;
  }

  /// 获取所有数据
  Future<Map<String, Object>> getAll() {
    throw UnimplementedError('getAll() has not been implemented.');
  }

  /// 移除指定 key
  Future<bool> remove(String key) {
    throw UnimplementedError('remove() has not been implemented.');
  }

  /// 清空所有数据
  Future<bool> clear() {
    throw UnimplementedError('clear() has not been implemented.');
  }

  /// 设置 String 值
  Future<bool> setValue(String valueType, String key, Object value) {
    throw UnimplementedError('setValue() has not been implemented.');
  }
}

2.3 MethodChannel 实现

// packages/shared_preferences_platform_interface/lib/method_channel_shared_preferences.dart

import 'package:flutter/services.dart';
import 'shared_preferences_platform_interface.dart';

/// 基于 MethodChannel 的实现
class MethodChannelSharedPreferencesStore extends SharedPreferencesStorePlatform {
  
  final MethodChannel channel = const MethodChannel('plugins.flutter.io/shared_preferences');

  
  Future<Map<String, Object>> getAll() async {
    final Map<String, Object>? preferences =
        await channel.invokeMapMethod<String, Object>('getAll');
    return preferences ?? <String, Object>{};
  }

  
  Future<bool> remove(String key) async {
    return await channel.invokeMethod<bool>('remove', <String, dynamic>{
      'key': key,
    }) ?? false;
  }

  
  Future<bool> clear() async {
    return await channel.invokeMethod<bool>('clear') ?? false;
  }

  
  Future<bool> setValue(String valueType, String key, Object value) async {
    return await channel.invokeMethod<bool>('set$valueType', <String, dynamic>{
      'key': key,
      'value': value,
    }) ?? false;
  }
}

三、应用层封装

3.1 SharedPreferences 类

// packages/shared_preferences/lib/shared_preferences.dart

import 'dart:async';
import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart';

/// SharedPreferences 本地存储
class SharedPreferences {
  SharedPreferences._(this._preferenceCache);

  static const String _prefix = 'flutter.';
  static SharedPreferences? _instance;
  static SharedPreferencesStorePlatform get _store =>
      SharedPreferencesStorePlatform.instance;

  final Map<String, Object> _preferenceCache;

  /// 获取实例(单例模式)
  static Future<SharedPreferences> getInstance() async {
    if (_instance != null) {
      return _instance!;
    }

    final Map<String, Object> preferencesMap = await _store.getAll();
    _instance = SharedPreferences._(preferencesMap);
    return _instance!;
  }

  /// 获取所有 key
  Set<String> getKeys() => _preferenceCache.keys.toSet();

  /// 获取 String 值
  String? getString(String key) {
    return _preferenceCache[_prefix + key] as String?;
  }

  /// 设置 String 值
  Future<bool> setString(String key, String value) {
    _preferenceCache[_prefix + key] = value;
    return _store.setValue('String', _prefix + key, value);
  }

  /// 获取 int 值
  int? getInt(String key) {
    return _preferenceCache[_prefix + key] as int?;
  }

  /// 设置 int 值
  Future<bool> setInt(String key, int value) {
    _preferenceCache[_prefix + key] = value;
    return _store.setValue('Int', _prefix + key, value);
  }

  /// 获取 double 值
  double? getDouble(String key) {
    return _preferenceCache[_prefix + key] as double?;
  }

  /// 设置 double 值
  Future<bool> setDouble(String key, double value) {
    _preferenceCache[_prefix + key] = value;
    return _store.setValue('Double', _prefix + key, value);
  }

  /// 获取 bool 值
  bool? getBool(String key) {
    return _preferenceCache[_prefix + key] as bool?;
  }

  /// 设置 bool 值
  Future<bool> setBool(String key, bool value) {
    _preferenceCache[_prefix + key] = value;
    return _store.setValue('Bool', _prefix + key, value);
  }

  /// 获取 List<String> 值
  List<String>? getStringList(String key) {
    final List<dynamic>? list = _preferenceCache[_prefix + key] as List<dynamic>?;
    return list?.cast<String>();
  }

  /// 设置 List<String> 值
  Future<bool> setStringList(String key, List<String> value) {
    _preferenceCache[_prefix + key] = value;
    return _store.setValue('StringList', _prefix + key, value);
  }

  /// 移除指定 key
  Future<bool> remove(String key) {
    _preferenceCache.remove(_prefix + key);
    return _store.remove(_prefix + key);
  }

  /// 清空所有数据
  Future<bool> clear() {
    _preferenceCache.clear();
    return _store.clear();
  }

  /// 检查 key 是否存在
  bool containsKey(String key) {
    return _preferenceCache.containsKey(_prefix + key);
  }

  /// 重新加载数据
  Future<void> reload() async {
    final Map<String, Object> preferences = await _store.getAll();
    _preferenceCache.clear();
    _preferenceCache.addAll(preferences);
  }
}

设计要点:

  1. 单例模式:全局只有一个实例,避免重复加载
  2. 内存缓存:读取时直接从缓存获取,提升性能
  3. key 前缀:添加 flutter. 前缀,避免与原生数据冲突
  4. 类型安全:每种类型都有独立的 get/set 方法

3.2 使用示例

// 保存数据
final prefs = await SharedPreferences.getInstance();
await prefs.setString('token', 'abc123');
await prefs.setInt('userId', 12345);
await prefs.setBool('isLoggedIn', true);

// 读取数据
final token = prefs.getString('token');
final userId = prefs.getInt('userId');
final isLoggedIn = prefs.getBool('isLoggedIn') ?? false;

// 删除数据
await prefs.remove('token');

// 清空所有数据
await prefs.clear();

四、Android 平台实现

4.1 Kotlin 插件代码

// packages/shared_preferences_android/android/src/main/kotlin/SharedPreferencesPlugin.kt

package io.flutter.plugins.sharedpreferences

import android.content.Context
import android.content.SharedPreferences
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
import io.flutter.plugin.common.MethodChannel.Result

class SharedPreferencesPlugin : FlutterPlugin, MethodCallHandler {
    private lateinit var channel: MethodChannel
    private lateinit var preferences: SharedPreferences

    companion object {
        private const val CHANNEL = "plugins.flutter.io/shared_preferences"
        private const val SHARED_PREFERENCES_NAME = "FlutterSharedPreferences"
    }

    override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) {
        channel = MethodChannel(binding.binaryMessenger, CHANNEL)
        channel.setMethodCallHandler(this)

        preferences = binding.applicationContext.getSharedPreferences(
            SHARED_PREFERENCES_NAME,
            Context.MODE_PRIVATE
        )
    }

    override fun onMethodCall(call: MethodCall, result: Result) {
        when (call.method) {
            "getAll" -> {
                result.success(getAllPrefs())
            }
            "setBool" -> {
                val key = call.argument<String>("key")!!
                val value = call.argument<Boolean>("value")!!
                result.success(preferences.edit().putBoolean(key, value).commit())
            }
            "setInt" -> {
                val key = call.argument<String>("key")!!
                val value = call.argument<Int>("value")!!
                result.success(preferences.edit().putInt(key, value).commit())
            }
            "setDouble" -> {
                val key = call.argument<String>("key")!!
                val value = call.argument<Double>("value")!!
                // Android 没有 putDouble,使用 Float
                result.success(preferences.edit().putFloat(key, value.toFloat()).commit())
            }
            "setString" -> {
                val key = call.argument<String>("key")!!
                val value = call.argument<String>("value")!!
                result.success(preferences.edit().putString(key, value).commit())
            }
            "setStringList" -> {
                val key = call.argument<String>("key")!!
                val value = call.argument<List<String>>("value")!!
                result.success(preferences.edit().putStringSet(key, value.toSet()).commit())
            }
            "remove" -> {
                val key = call.argument<String>("key")!!
                result.success(preferences.edit().remove(key).commit())
            }
            "clear" -> {
                result.success(preferences.edit().clear().commit())
            }
            else -> {
                result.notImplemented()
            }
        }
    }

    private fun getAllPrefs(): Map<String, Any> {
        val allPrefs = preferences.all
        val result = mutableMapOf<String, Any>()

        for ((key, value) in allPrefs) {
            when (value) {
                is Boolean -> result[key] = value
                is Int -> result[key] = value
                is Long -> result[key] = value.toInt()
                is Float -> result[key] = value.toDouble()
                is String -> result[key] = value
                is Set<*> -> result[key] = value.toList()
            }
        }

        return result
    }

    override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
        channel.setMethodCallHandler(null)
    }
}

关键点:

  1. SharedPreferences API:Android 原生键值对存储
  2. commit() vs apply():commit() 同步写入,apply() 异步写入
  3. 类型转换:Android 没有 double 类型,使用 float

本篇小结(第一部分)

这部分我们完成了:

  1. ✅ 本地存储方案对比
  2. ✅ 插件架构设计
  3. ✅ 接口层设计
  4. ✅ 应用层封装(SharedPreferences 类)
  5. ✅ Android 平台实现

下一部分我会继续讲解 iOS 和 HarmonyOS 的实现。


五、iOS 平台实现

5.1 Swift 插件代码

// packages/shared_preferences_foundation/ios/Classes/SharedPreferencesPlugin.swift

import Flutter
import UIKit

public class SharedPreferencesPlugin: NSObject, FlutterPlugin {
    public static func register(with registrar: FlutterPluginRegistrar) {
        let channel = FlutterMethodChannel(
            name: "plugins.flutter.io/shared_preferences",
            binaryMessenger: registrar.messenger()
        )
        let instance = SharedPreferencesPlugin()
        registrar.addMethodCallDelegate(instance, channel: channel)
    }

    public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
        let userDefaults = UserDefaults.standard

        switch call.method {
        case "getAll":
            result(getAllPrefs())

        case "setBool":
            guard let args = call.arguments as? [String: Any],
                  let key = args["key"] as? String,
                  let value = args["value"] as? Bool else {
                result(FlutterError(code: "INVALID_ARGS", message: "Invalid arguments", details: nil))
                return
            }
            userDefaults.set(value, forKey: key)
            result(userDefaults.synchronize())

        case "setInt":
            guard let args = call.arguments as? [String: Any],
                  let key = args["key"] as? String,
                  let value = args["value"] as? Int else {
                result(FlutterError(code: "INVALID_ARGS", message: "Invalid arguments", details: nil))
                return
            }
            userDefaults.set(value, forKey: key)
            result(userDefaults.synchronize())

        case "setDouble":
            guard let args = call.arguments as? [String: Any],
                  let key = args["key"] as? String,
                  let value = args["value"] as? Double else {
                result(FlutterError(code: "INVALID_ARGS", message: "Invalid arguments", details: nil))
                return
            }
            userDefaults.set(value, forKey: key)
            result(userDefaults.synchronize())

        case "setString":
            guard let args = call.arguments as? [String: Any],
                  let key = args["key"] as? String,
                  let value = args["value"] as? String else {
                result(FlutterError(code: "INVALID_ARGS", message: "Invalid arguments", details: nil))
                return
            }
            userDefaults.set(value, forKey: key)
            result(userDefaults.synchronize())

        case "setStringList":
            guard let args = call.arguments as? [String: Any],
                  let key = args["key"] as? String,
                  let value = args["value"] as? [String] else {
                result(FlutterError(code: "INVALID_ARGS", message: "Invalid arguments", details: nil))
                return
            }
            userDefaults.set(value, forKey: key)
            result(userDefaults.synchronize())

        case "remove":
            guard let args = call.arguments as? [String: Any],
                  let key = args["key"] as? String else {
                result(FlutterError(code: "INVALID_ARGS", message: "Invalid arguments", details: nil))
                return
            }
            userDefaults.removeObject(forKey: key)
            result(userDefaults.synchronize())

        case "clear":
            let domain = Bundle.main.bundleIdentifier!
            userDefaults.removePersistentDomain(forName: domain)
            result(userDefaults.synchronize())

        default:
            result(FlutterMethodNotImplemented)
        }
    }

    private func getAllPrefs() -> [String: Any] {
        let userDefaults = UserDefaults.standard
        var result: [String: Any] = [:]

        // 获取所有键值对
        if let allPrefs = userDefaults.dictionaryRepresentation() as? [String: Any] {
            for (key, value) in allPrefs {
                // 只返回 flutter. 前缀的数据
                if key.hasPrefix("flutter.") {
                    result[key] = value
                }
            }
        }

        return result
    }
}

关键点:

  1. UserDefaults API:iOS 原生键值对存储
  2. synchronize():强制同步到磁盘(iOS 12+ 已废弃,但为了兼容性保留)
  3. 类型支持:iOS 原生支持所有基本类型

六、HarmonyOS 平台实现

6.1 ArkTS 插件代码

// packages/shared_preferences_ohos/ohos/src/main/ets/SharedPreferencesPlugin.ets

import { FlutterPlugin, MethodCall, MethodChannel, MethodResult } from '@ohos/flutter_ohos';
import dataPreferences from '@ohos.data.preferences';
import { BusinessError } from '@ohos.base';

export class SharedPreferencesPlugin implements FlutterPlugin {
  private channel: MethodChannel | null = null;
  private preferences: dataPreferences.Preferences | null = null;

  getUniqueClassName(): string {
    return "SharedPreferencesPlugin";
  }

  async onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding): Promise<void> {
    this.channel = new MethodChannel(
      binding.getBinaryMessenger(),
      'plugins.flutter.io/shared_preferences'
    );
    this.channel.setMethodCallHandler(this.onMethodCall.bind(this));

    // 初始化 Preferences
    try {
      const context = binding.getApplicationContext();
      this.preferences = await dataPreferences.getPreferences(context, 'FlutterSharedPreferences');
      console.info('[SharedPreferences] Preferences initialized');
    } catch (err) {
      console.error(`[SharedPreferences] Failed to initialize: ${(err as Error).message}`);
    }
  }

  async onMethodCall(call: MethodCall, result: MethodResult): Promise<void> {
    const method = call.method;

    try {
      if (this.preferences === null) {
        result.error('ERROR', 'Preferences not initialized', null);
        return;
      }

      switch (method) {
        case 'getAll':
          await this.getAll(result);
          break;
        case 'setBool':
          await this.setBool(call, result);
          break;
        case 'setInt':
          await this.setInt(call, result);
          break;
        case 'setDouble':
          await this.setDouble(call, result);
          break;
        case 'setString':
          await this.setString(call, result);
          break;
        case 'setStringList':
          await this.setStringList(call, result);
          break;
        case 'remove':
          await this.remove(call, result);
          break;
        case 'clear':
          await this.clear(result);
          break;
        default:
          result.notImplemented();
      }
    } catch (err) {
      result.error('ERROR', (err as Error).message, null);
    }
  }

  private async getAll(result: MethodResult): Promise<void> {
    if (this.preferences === null) {
      result.error('ERROR', 'Preferences not initialized', null);
      return;
    }

    try {
      const allData: Record<string, any> = {};
      const keys = await this.preferences.getAll();

      for (const key in keys) {
        allData[key] = keys[key];
      }

      result.success(allData);
    } catch (err) {
      result.error('ERROR', (err as Error).message, null);
    }
  }

  private async setBool(call: MethodCall, result: MethodResult): Promise<void> {
    const args = call.args as Record<string, any>;
    const key = args['key'] as string;
    const value = args['value'] as boolean;

    if (this.preferences !== null) {
      await this.preferences.put(key, value);
      await this.preferences.flush();
      result.success(true);
    } else {
      result.error('ERROR', 'Preferences not initialized', null);
    }
  }

  private async setInt(call: MethodCall, result: MethodResult): Promise<void> {
    const args = call.args as Record<string, any>;
    const key = args['key'] as string;
    const value = args['value'] as number;

    if (this.preferences !== null) {
      await this.preferences.put(key, value);
      await this.preferences.flush();
      result.success(true);
    } else {
      result.error('ERROR', 'Preferences not initialized', null);
    }
  }

  private async setDouble(call: MethodCall, result: MethodResult): Promise<void> {
    const args = call.args as Record<string, any>;
    const key = args['key'] as string;
    const value = args['value'] as number;

    if (this.preferences !== null) {
      await this.preferences.put(key, value);
      await this.preferences.flush();
      result.success(true);
    } else {
      result.error('ERROR', 'Preferences not initialized', null);
    }
  }

  private async setString(call: MethodCall, result: MethodResult): Promise<void> {
    const args = call.args as Record<string, any>;
    const key = args['key'] as string;
    const value = args['value'] as string;

    if (this.preferences !== null) {
      await this.preferences.put(key, value);
      await this.preferences.flush();
      result.success(true);
    } else {
      result.error('ERROR', 'Preferences not initialized', null);
    }
  }

  private async setStringList(call: MethodCall, result: MethodResult): Promise<void> {
    const args = call.args as Record<string, any>;
    const key = args['key'] as string;
    const value = args['value'] as string[];

    if (this.preferences !== null) {
      // HarmonyOS Preferences 支持数组类型
      await this.preferences.put(key, value);
      await this.preferences.flush();
      result.success(true);
    } else {
      result.error('ERROR', 'Preferences not initialized', null);
    }
  }

  private async remove(call: MethodCall, result: MethodResult): Promise<void> {
    const args = call.args as Record<string, any>;
    const key = args['key'] as string;

    if (this.preferences !== null) {
      await this.preferences.delete(key);
      await this.preferences.flush();
      result.success(true);
    } else {
      result.error('ERROR', 'Preferences not initialized', null);
    }
  }

  private async clear(result: MethodResult): Promise<void> {
    if (this.preferences !== null) {
      await this.preferences.clear();
      await this.preferences.flush();
      result.success(true);
    } else {
      result.error('ERROR', 'Preferences not initialized', null);
    }
  }

  onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding): void {
    if (this.channel !== null) {
      this.channel.setMethodCallHandler(null);
    }
  }
}

关键点:

  1. Preferences API:HarmonyOS 使用 @ohos.data.preferences
  2. 异步操作:所有操作都是异步的,需要 await
  3. flush() 调用:修改后必须调用 flush() 持久化
  4. 类型支持:HarmonyOS 原生支持数组类型

七、实战应用

7.1 封装工具类

在 CleanMark AI 项目中,我们封装了一个工具类来简化使用:

// lib/core/utils/app_prefs.dart

import 'package:shared_preferences/shared_preferences.dart';
import '../../features/auth/user_model.dart';

/// 应用本地存储工具类
class AppPrefs {
  AppPrefs._();

  static SharedPreferences? _prefs;

  /// 初始化(在 main 函数中调用)
  static Future<void> init() async {
    _prefs = await SharedPreferences.getInstance();
  }

  /// 获取实例
  static SharedPreferences get instance {
    if (_prefs == null) {
      throw StateError('AppPrefs not initialized. Call AppPrefs.init() first.');
    }
    return _prefs!;
  }

  // ==================== 引导页 ====================

  /// 引导页是否已完成
  static Future<bool> isOnboardingDone() async {
    return instance.getBool('onboarding_done') ?? false;
  }

  /// 标记引导页已完成
  static Future<void> setOnboardingDone() async {
    await instance.setBool('onboarding_done', true);
  }

  // ==================== 用户信息 ====================

  /// 保存用户信息
  static Future<void> saveUser(UserModel user) async {
    await instance.setString('user_id', user.id);
    await instance.setString('user_email', user.email);
    await instance.setString('user_nickname', user.nickname ?? '');
    await instance.setInt('user_credits', user.credits);
  }

  /// 加载用户信息
  static Future<UserModel?> loadUser() async {
    final userId = instance.getString('user_id');
    if (userId == null) return null;

    return UserModel(
      id: userId,
      email: instance.getString('user_email') ?? '',
      nickname: instance.getString('user_nickname'),
      credits: instance.getInt('user_credits') ?? 0,
    );
  }

  /// 清除用户信息
  static Future<void> clearUser() async {
    await instance.remove('user_id');
    await instance.remove('user_email');
    await instance.remove('user_nickname');
    await instance.remove('user_credits');
  }

  // ==================== Token ====================

  /// 保存 Token
  static Future<void> saveToken(String token) async {
    await instance.setString('auth_token', token);
  }

  /// 获取 Token
  static String? getToken() {
    return instance.getString('auth_token');
  }

  /// 清除 Token
  static Future<void> clearToken() async {
    await instance.remove('auth_token');
  }

  // ==================== 应用配置 ====================

  /// 保存语言设置
  static Future<void> setLanguage(String languageCode) async {
    await instance.setString('language', languageCode);
  }

  /// 获取语言设置
  static String getLanguage() {
    return instance.getString('language') ?? 'zh';
  }
}

7.2 在项目中使用

// lib/main.dart

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  // 初始化本地存储
  await AppPrefs.init();

  // 检查引导页状态
  final skipOnboarding = await AppPrefs.isOnboardingDone();

  // 检查登录状态
  final token = AppPrefs.getToken();
  final isLoggedIn = token != null && token.isNotEmpty;

  runApp(MyApp(
    skipOnboarding: skipOnboarding,
    isLoggedIn: isLoggedIn,
  ));
}

八、性能优化

8.1 批量操作优化

// ❌ 不好的做法:多次写入
await prefs.setString('key1', 'value1');
await prefs.setString('key2', 'value2');
await prefs.setString('key3', 'value3');

// ✅ 好的做法:批量写入(减少 I/O 次数)
await Future.wait([
  prefs.setString('key1', 'value1'),
  prefs.setString('key2', 'value2'),
  prefs.setString('key3', 'value3'),
]);

8.2 避免频繁读写

// ❌ 不好的做法:每次都读取
Widget build(BuildContext context) {
  final prefs = await SharedPreferences.getInstance();
  final token = prefs.getString('token');
  return Text(token ?? '');
}

// ✅ 好的做法:缓存到内存
class MyWidget extends StatefulWidget {
  
  State<MyWidget> createState() => _MyWidgetState();
}

class _MyWidgetState extends State<MyWidget> {
  String? _token;

  
  void initState() {
    super.initState();
    _loadToken();
  }

  Future<void> _loadToken() async {
    final prefs = await SharedPreferences.getInstance();
    setState(() {
      _token = prefs.getString('token');
    });
  }

  
  Widget build(BuildContext context) {
    return Text(_token ?? '');
  }
}

本篇完整小结

这篇文章我们完成了:

  1. ✅ 本地存储方案对比
  2. ✅ 插件架构设计(联邦插件模式)
  3. ✅ 接口层设计
  4. ✅ 应用层封装(SharedPreferences 类)
  5. ✅ Android 平台实现(SharedPreferences)
  6. ✅ iOS 平台实现(UserDefaults)
  7. ✅ HarmonyOS 平台实现(Preferences)
  8. ✅ 实战应用(工具类封装)
  9. ✅ 性能优化建议

关键要点:

  • 三端使用各自的原生存储 API
  • 采用单例模式 + 内存缓存提升性能
  • 添加 key 前缀避免冲突
  • HarmonyOS 需要调用 flush() 持久化
  • 批量操作减少 I/O 次数

思考题

  1. 为什么要使用单例模式?
  2. SharedPreferences 适合存储什么类型的数据?不适合存储什么?
  3. 如何实现数据加密存储?

下一篇预告:第07篇 - 视频播放器的 HarmonyOS 适配

更多推荐