一文走进HarmonyOS开发中的分布式首选项

举报
Jack20 发表于 2026/06/19 22:50:18 2026/06/19
【摘要】 首选项(Preferences)是应用配置的"小金库":主题设置、字体大小、通知开关…这些个性化配置如何在多设备间同步?让用户在手机上设置深色模式,平板自动切换,这才是真正的"无缝体验"。 一、背景与动机:为什么需要分布式首选项? 1.1 传统首选项的局限传统首选项(SharedPreferences、UserDefaults等)只存储在本地设备:场景1:主题设置用户在手机上设置了深色主题打...

首选项(Preferences)是应用配置的"小金库":主题设置、字体大小、通知开关…这些个性化配置如何在多设备间同步?让用户在手机上设置深色模式,平板自动切换,这才是真正的"无缝体验"。

一、背景与动机:为什么需要分布式首选项?

1.1 传统首选项的局限

传统首选项(SharedPreferences、UserDefaults等)只存储在本地设备:

场景1:主题设置

  • 用户在手机上设置了深色主题
  • 打开平板应用,还是浅色主题
  • 需要重新设置,体验割裂

场景2:字体大小

  • 老年用户在手机上调大了字体
  • 在智慧屏上看视频,字幕字体还是默认大小
  • 每个设备都要设置一遍

场景3:通知偏好

  • 用户在手机上关闭了某类通知
  • 平板上还在推送同类通知
  • 设置不同步,造成困扰

1.2 分布式首选项的价值

分布式首选项让配置"一次设置,处处生效":
图片.png

二、核心原理:分布式首选项架构

2.1 数据结构

分布式首选项本质是一个支持跨设备同步的键值对存储:

interface DistributedPreferences {
  // 基础操作
  get(key: string, defaultValue?: any): any;
  put(key: string, value: any): Promise<void>;
  delete(key: string): Promise<void>;
  clear(): Promise<void>;
  
  // 同步操作
  sync(): Promise<void>;
  setSyncEnabled(key: string, enabled: boolean): void;
  
  // 监听操作
  on(key: string, callback: (value: any) => void): void;
  off(key: string, callback?: (value: any) => void): void;
}

2.2 同步策略

不同类型的配置采用不同的同步策略:

配置类型 同步策略 原因
主题设置 实时同步 视觉一致性要求高
字体大小 实时同步 可访问性设置重要
通知偏好 延迟同步 非实时需求
语言设置 实时同步 影响内容展示
设备音量 不同步 设备特定配置
网络配置 不同步 设备特定配置

2.3 冲突处理

配置冲突通常采用"最新优先"策略:

sequenceDiagram
    participant DeviceA as 设备A
    participant Cloud as 同步服务
    participant DeviceB as 设备B
    
    Note over DeviceA: 用户设置主题=dark<br/>时间=10:00:00
    DeviceA->>Cloud: put('theme', 'dark', 10:00:00)
    
    Note over DeviceB: 用户设置主题=light<br/>时间=10:00:01
    DeviceB->>Cloud: put('theme', 'light', 10:00:01)
    
    Cloud->>Cloud: 检测冲突<br/>时间戳比较
    
    Cloud-->>DeviceA: 最终值: light (更新)
    Cloud-->>DeviceB: 最终值: light
    
    Note over DeviceA,DeviceB: 最终一致性达成
    
    classDef primary fill:#4A90E2,stroke:#2E5C8A,stroke-width:2px,color:#fff
    classDef warning fill:#F5A623,stroke:#C17D10,stroke-width:2px,color:#fff
    classDef info fill:#7ED321,stroke:#5BA318,stroke-width:2px,color:#fff
    
    class DeviceA,DeviceB primary
    class Cloud warning

三、代码实战:实现分布式首选项

3.1 基础示例:创建和使用分布式首选项

import preferences from '@ohos.data.preferences';
import distributedData from '@ohos.data.distributedData';
import { BusinessError } from '@ohos.base';

/**
 * 分布式首选项管理器
 * 提供配置的存储、读取、同步能力
 */
export class DistributedPreferencesManager {
  private preferences: preferences.Preferences | null = null;
  private kvStore: distributedData.KVStore | null = null;
  private readonly STORE_NAME = 'distributed_preferences';
  
  /**
   * 初始化分布式首选项
   */
  async init(context: Context): Promise<void> {
    try {
      // 创建本地首选项
      this.preferences = await preferences.getPreferences(context, this.STORE_NAME);
      
      // 创建分布式KV存储(用于同步)
      const kvManager = distributedData.createKVManager({
        bundleName: context.applicationInfo.name,
        userId: 0
      });
      
      this.kvStore = await kvManager.getKVStore('preferences_sync', {
        createIfMissing: true,
        autoSync: true,
        kvStoreType: distributedData.KVStoreType.DEVICE_COLLABORATION,
        securityLevel: distributedData.SecurityLevel.S1
      });
      
      // 设置远端数据监听
      this.setupRemoteListener();
      
      console.info('[DistributedPrefs] 初始化成功');
    } catch (error) {
      const err = error as BusinessError;
      console.error(`[DistributedPrefs] 初始化失败: ${err.message}`);
      throw error;
    }
  }
  
  /**
   * 获取配置值
   * @param key 配置键
   * @param defaultValue 默认值
   */
  async get<T>(key: string, defaultValue: T): Promise<T> {
    if (!this.preferences) {
      throw new Error('首选项未初始化');
    }
    
    try {
      const value = await this.preferences.get(key, defaultValue);
      console.info(`[DistributedPrefs] 读取配置: ${key} = ${value}`);
      return value as T;
    } catch (error) {
      console.error(`[DistributedPrefs] 读取失败: ${error}`);
      return defaultValue;
    }
  }
  
  /**
   * 设置配置值
   * @param key 配置键
   * @param value 配置值
   * @param sync 是否同步到其他设备
   */
  async put<T>(key: string, value: T, sync: boolean = true): Promise<void> {
    if (!this.preferences) {
      throw new Error('首选项未初始化');
    }
    
    try {
      // 写入本地
      await this.preferences.put(key, value);
      await this.preferences.flush();
      
      console.info(`[DistributedPrefs] 写入配置: ${key} = ${value}`);
      
      // 同步到远端
      if (sync && this.kvStore) {
        const syncData = {
          key,
          value,
          timestamp: Date.now(),
          deviceId: await this.getDeviceId()
        };
        
        await this.kvStore.put(key, JSON.stringify(syncData));
        console.info(`[DistributedPrefs] 配置已同步: ${key}`);
      }
    } catch (error) {
      console.error(`[DistributedPrefs] 写入失败: ${error}`);
      throw error;
    }
  }
  
  /**
   * 删除配置
   */
  async delete(key: string, sync: boolean = true): Promise<void> {
    if (!this.preferences) {
      throw new Error('首选项未初始化');
    }
    
    try {
      await this.preferences.delete(key);
      await this.preferences.flush();
      
      console.info(`[DistributedPrefs] 删除配置: ${key}`);
      
      if (sync && this.kvStore) {
        await this.kvStore.delete(key);
      }
    } catch (error) {
      console.error(`[DistributedPrefs] 删除失败: ${error}`);
      throw error;
    }
  }
  
  /**
   * 清空所有配置
   */
  async clear(sync: boolean = true): Promise<void> {
    if (!this.preferences) {
      throw new Error('首选项未初始化');
    }
    
    try {
      await this.preferences.clear();
      await this.preferences.flush();
      
      console.info('[DistributedPrefs] 清空所有配置');
      
      if (sync && this.kvStore) {
        // 获取所有key并删除
        const allKeys = await this.getAllSyncKeys();
        for (const key of allKeys) {
          await this.kvStore.delete(key);
        }
      }
    } catch (error) {
      console.error(`[DistributedPrefs] 清空失败: ${error}`);
      throw error;
    }
  }
  
  /**
   * 设置远端数据监听
   */
  private setupRemoteListener(): void {
    if (!this.kvStore) return;
    
    this.kvStore.on('dataChange', distributedData.SubscribeType.SUBSCRIBE_TYPE_REMOTE,
      async (data: distributedData.ChangeNotification) => {
        console.info('[DistributedPrefs] 收到远端配置变更');
        
        // 处理更新的配置
        for (const entry of data.updateEntries) {
          await this.applyRemoteChange(entry.key, entry.value as string);
        }
        
        // 处理删除的配置
        for (const entry of data.deleteEntries) {
          await this.preferences?.delete(entry.key);
        }
        
        await this.preferences?.flush();
      });
  }
  
  /**
   * 应用远端变更
   */
  private async applyRemoteChange(key: string, valueStr: string): Promise<void> {
    if (!this.preferences) return;
    
    try {
      const remoteData = JSON.parse(valueStr);
      
      // 读取本地时间戳
      const localDataStr = await this.preferences.get(key, '');
      if (localDataStr) {
        const localData = JSON.parse(localDataStr as string);
        
        // 时间戳比较:远端更新才应用
        if (remoteData.timestamp <= localData.timestamp) {
          console.info(`[DistributedPrefs] 本地更新,忽略远端变更: ${key}`);
          return;
        }
      }
      
      // 应用远端值
      await this.preferences.put(key, remoteData.value);
      console.info(`[DistributedPrefs] 应用远端配置: ${key} = ${remoteData.value}`);
      
      // 触发变更通知
      this.notifyChange(key, remoteData.value);
    } catch (error) {
      console.error(`[DistributedPrefs] 应用远端变更失败: ${error}`);
    }
  }
  
  /**
   * 获取所有同步的key
   */
  private async getAllSyncKeys(): Promise<string[]> {
    if (!this.kvStore) return [];
    
    // 简化处理,实际应通过KVStore的API获取
    return [];
  }
  
  /**
   * 获取设备ID
   */
  private async getDeviceId(): Promise<string> {
    return 'device_' + Date.now();
  }
  
  /**
   * 变更通知(子类可重写)
   */
  protected notifyChange(key: string, value: any): void {
    console.info(`[DistributedPrefs] 配置变更通知: ${key} = ${value}`);
  }
}

3.2 进阶示例:类型安全的配置管理

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

/**
 * 应用配置接口定义
 * 强类型约束,避免拼写错误
 */
interface AppConfig {
  // 外观设置
  theme: 'light' | 'dark' | 'auto';
  fontSize: number;              // 12-24
  language: string;              // 'zh-CN', 'en-US'
  
  // 通知设置
  notificationEnabled: boolean;
  notificationSound: boolean;
  notificationVibration: boolean;
  
  // 隐私设置
  analyticsEnabled: boolean;
  crashReportEnabled: boolean;
  
  // 功能设置
  autoPlayVideo: boolean;
  imageQuality: 'low' | 'medium' | 'high' | 'auto';
  downloadWiFiOnly: boolean;
}

/**
 * 配置项元数据
 */
interface ConfigMeta {
  key: keyof AppConfig;
  defaultValue: any;
  sync: boolean;           // 是否同步
  type: 'string' | 'number' | 'boolean';
  validator?: (value: any) => boolean;
}

/**
 * 类型安全的配置管理器
 */
export class TypedConfigManager extends DistributedPreferencesManager {
  // 配置项定义
  private static readonly CONFIG_META: ConfigMeta[] = [
    {
      key: 'theme',
      defaultValue: 'auto',
      sync: true,
      type: 'string',
      validator: (v) => ['light', 'dark', 'auto'].includes(v)
    },
    {
      key: 'fontSize',
      defaultValue: 16,
      sync: true,
      type: 'number',
      validator: (v) => v >= 12 && v <= 24
    },
    {
      key: 'language',
      defaultValue: 'zh-CN',
      sync: true,
      type: 'string'
    },
    {
      key: 'notificationEnabled',
      defaultValue: true,
      sync: true,
      type: 'boolean'
    },
    {
      key: 'notificationSound',
      defaultValue: true,
      sync: true,
      type: 'boolean'
    },
    {
      key: 'notificationVibration',
      defaultValue: false,
      sync: true,
      type: 'boolean'
    },
    {
      key: 'analyticsEnabled',
      defaultValue: true,
      sync: true,
      type: 'boolean'
    },
    {
      key: 'crashReportEnabled',
      defaultValue: true,
      sync: true,
      type: 'boolean'
    },
    {
      key: 'autoPlayVideo',
      defaultValue: false,
      sync: true,
      type: 'boolean'
    },
    {
      key: 'imageQuality',
      defaultValue: 'auto',
      sync: true,
      type: 'string',
      validator: (v) => ['low', 'medium', 'high', 'auto'].includes(v)
    },
    {
      key: 'downloadWiFiOnly',
      defaultValue: true,
      sync: true,
      type: 'boolean'
    }
  ];
  
  // 变更监听器
  private changeListeners: Map<keyof AppConfig, Set<(value: any) => void>> = new Map();
  
  /**
   * 获取配置值(类型安全)
   */
  async getConfig<K extends keyof AppConfig>(key: K): Promise<AppConfig[K]> {
    const meta = this.getMeta(key);
    const value = await this.get(key, meta.defaultValue);
    
    // 验证
    if (meta.validator && !meta.validator(value)) {
      console.warn(`[TypedConfig] 配置值验证失败,使用默认值: ${key}`);
      return meta.defaultValue;
    }
    
    return value;
  }
  
  /**
   * 设置配置值(类型安全)
   */
  async setConfig<K extends keyof AppConfig>(key: K, value: AppConfig[K]): Promise<void> {
    const meta = this.getMeta(key);
    
    // 类型检查
    if (!this.checkType(value, meta.type)) {
      throw new Error(`配置值类型错误: ${key} 期望 ${meta.type}`);
    }
    
    // 验证
    if (meta.validator && !meta.validator(value)) {
      throw new Error(`配置值验证失败: ${key}`);
    }
    
    // 写入
    await this.put(key, value, meta.sync);
    
    // 触发监听器
    this.triggerListeners(key, value);
  }
  
  /**
   * 批量获取配置
   */
  async getAllConfig(): Promise<AppConfig> {
    const config: Partial<AppConfig> = {};
    
    for (const meta of TypedConfigManager.CONFIG_META) {
      config[meta.key] = await this.getConfig(meta.key);
    }
    
    return config as AppConfig;
  }
  
  /**
   * 批量设置配置
   */
  async setAllConfig(config: Partial<AppConfig>): Promise<void> {
    for (const [key, value] of Object.entries(config)) {
      await this.setConfig(key as keyof AppConfig, value);
    }
  }
  
  /**
   * 重置为默认值
   */
  async resetToDefault(key?: keyof AppConfig): Promise<void> {
    if (key) {
      const meta = this.getMeta(key);
      await this.setConfig(key, meta.defaultValue);
    } else {
      for (const meta of TypedConfigManager.CONFIG_META) {
        await this.setConfig(meta.key, meta.defaultValue);
      }
    }
    
    console.info('[TypedConfig] 已重置为默认值');
  }
  
  /**
   * 注册变更监听器
   */
  onChange<K extends keyof AppConfig>(key: K, callback: (value: AppConfig[K]) => void): void {
    if (!this.changeListeners.has(key)) {
      this.changeListeners.set(key, new Set());
    }
    
    this.changeListeners.get(key)!.add(callback);
  }
  
  /**
   * 移除变更监听器
   */
  offChange<K extends keyof AppConfig>(key: K, callback?: (value: AppConfig[K]) => void): void {
    if (!callback) {
      this.changeListeners.delete(key);
    } else {
      this.changeListeners.get(key)?.delete(callback);
    }
  }
  
  /**
   * 触发监听器
   */
  private triggerListeners(key: keyof AppConfig, value: any): void {
    const listeners = this.changeListeners.get(key);
    if (listeners) {
      for (const callback of listeners) {
        try {
          callback(value);
        } catch (error) {
          console.error(`[TypedConfig] 监听器执行失败: ${error}`);
        }
      }
    }
  }
  
  /**
   * 获取配置元数据
   */
  private getMeta(key: keyof AppConfig): ConfigMeta {
    const meta = TypedConfigManager.CONFIG_META.find(m => m.key === key);
    if (!meta) {
      throw new Error(`未知的配置项: ${key}`);
    }
    return meta;
  }
  
  /**
   * 类型检查
   */
  private checkType(value: any, type: string): boolean {
    switch (type) {
      case 'string':
        return typeof value === 'string';
      case 'number':
        return typeof value === 'number';
      case 'boolean':
        return typeof value === 'boolean';
      default:
        return true;
    }
  }
  
  /**
   * 重写父类的变更通知
   */
  protected notifyChange(key: string, value: any): void {
    this.triggerListeners(key as keyof AppConfig, value);
  }
}

3.3 高级示例:配置分组与迁移

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

/**
 * 配置分组枚举
 */
enum ConfigGroup {
  APPEARANCE = 'appearance',      // 外观
  NOTIFICATION = 'notification',  // 通知
  PRIVACY = 'privacy',           // 隐私
  FEATURE = 'feature',           // 功能
  DEVICE = 'device'              // 设备特定(不同步)
}

/**
 * 分组配置项定义
 */
interface GroupedConfigMeta extends ConfigMeta {
  group: ConfigGroup;
  description: string;           // 配置描述
  version: number;               // 配置版本(用于迁移)
}

/**
 * 高级配置管理器
 * 支持分组、迁移、导入导出
 */
export class AdvancedConfigManager extends TypedConfigManager {
  // 分组配置定义
  private static readonly GROUPED_CONFIG: GroupedConfigMeta[] = [
    // 外观组
    {
      key: 'theme',
      group: ConfigGroup.APPEARANCE,
      defaultValue: 'auto',
      sync: true,
      type: 'string',
      description: '应用主题',
      version: 1
    },
    {
      key: 'fontSize',
      group: ConfigGroup.APPEARANCE,
      defaultValue: 16,
      sync: true,
      type: 'number',
      description: '字体大小',
      version: 1
    },
    {
      key: 'language',
      group: ConfigGroup.APPEARANCE,
      defaultValue: 'zh-CN',
      sync: true,
      type: 'string',
      description: '应用语言',
      version: 1
    },
    
    // 通知组
    {
      key: 'notificationEnabled',
      group: ConfigGroup.NOTIFICATION,
      defaultValue: true,
      sync: true,
      type: 'boolean',
      description: '通知开关',
      version: 1
    },
    {
      key: 'notificationSound',
      group: ConfigGroup.NOTIFICATION,
      defaultValue: true,
      sync: true,
      type: 'boolean',
      description: '通知声音',
      version: 1
    },
    
    // 隐私组
    {
      key: 'analyticsEnabled',
      group: ConfigGroup.PRIVACY,
      defaultValue: true,
      sync: true,
      type: 'boolean',
      description: '数据分析',
      version: 1
    },
    
    // 功能组
    {
      key: 'autoPlayVideo',
      group: ConfigGroup.FEATURE,
      defaultValue: false,
      sync: true,
      type: 'boolean',
      description: '自动播放视频',
      version: 1
    },
    {
      key: 'imageQuality',
      group: ConfigGroup.FEATURE,
      defaultValue: 'auto',
      sync: true,
      type: 'string',
      description: '图片质量',
      version: 1
    }
  ];
  
  /**
   * 按组获取配置
   */
  async getConfigByGroup(group: ConfigGroup): Promise<Record<string, any>> {
    const config: Record<string, any> = {};
    
    const groupConfigs = AdvancedConfigManager.GROUPED_CONFIG.filter(c => c.group === group);
    
    for (const meta of groupConfigs) {
      config[meta.key] = await this.getConfig(meta.key as keyof AppConfig);
    }
    
    return config;
  }
  
  /**
   * 按组设置配置
   */
  async setConfigByGroup(group: ConfigGroup, config: Record<string, any>): Promise<void> {
    for (const [key, value] of Object.entries(config)) {
      const meta = AdvancedConfigManager.GROUPED_CONFIG.find(c => c.key === key);
      if (meta && meta.group === group) {
        await this.setConfig(key as keyof AppConfig, value);
      }
    }
  }
  
  /**
   * 导出配置(用于备份)
   */
  async exportConfig(groups?: ConfigGroup[]): Promise<string> {
    const exportData: any = {
      version: 1,
      timestamp: Date.now(),
      deviceId: await this.getDeviceId(),
      config: {}
    };
    
    const targetGroups = groups || Object.values(ConfigGroup);
    
    for (const group of targetGroups) {
      exportData.config[group] = await this.getConfigByGroup(group);
    }
    
    return JSON.stringify(exportData, null, 2);
  }
  
  /**
   * 导入配置(用于恢复)
   */
  async importConfig(jsonStr: string, merge: boolean = true): Promise<ImportResult> {
    const result: ImportResult = {
      success: true,
      imported: 0,
      skipped: 0,
      errors: []
    };
    
    try {
      const importData = JSON.parse(jsonStr);
      
      // 版本检查
      if (importData.version !== 1) {
        throw new Error(`不支持的配置版本: ${importData.version}`);
      }
      
      // 导入各组配置
      for (const [group, config] of Object.entries(importData.config)) {
        const groupEnum = group as ConfigGroup;
        
        for (const [key, value] of Object.entries(config as Record<string, any>)) {
          try {
            const meta = AdvancedConfigManager.GROUPED_CONFIG.find(c => c.key === key);
            
            if (!meta) {
              result.skipped++;
              continue;
            }
            
            // 如果不合并,先检查是否已存在
            if (!merge) {
              const existing = await this.getConfig(key as keyof AppConfig);
              if (existing !== meta.defaultValue) {
                result.skipped++;
                continue;
              }
            }
            
            // 执行导入
            await this.setConfig(key as keyof AppConfig, value);
            result.imported++;
            
          } catch (error) {
            result.errors.push({ key, error: String(error) });
          }
        }
      }
      
      console.info(`[AdvancedConfig] 导入完成: 成功 ${result.imported}, 跳过 ${result.skipped}, 错误 ${result.errors.length}`);
      
    } catch (error) {
      result.success = false;
      result.errors.push({ key: 'global', error: String(error) });
    }
    
    return result;
  }
  
  /**
   * 配置迁移
   * 当配置结构变更时执行
   */
  async migrateConfig(fromVersion: number, toVersion: number): Promise<void> {
    console.info(`[AdvancedConfig] 开始迁移: ${fromVersion} -> ${toVersion}`);
    
    // 示例:从版本1迁移到版本2
    if (fromVersion === 1 && toVersion === 2) {
      // 读取旧配置
      const oldTheme = await this.getConfig('theme');
      
      // 迁移逻辑(示例:主题值变更)
      let newTheme = oldTheme;
      if (oldTheme === 'night') {
        newTheme = 'dark';
      }
      
      // 写入新配置
      await this.setConfig('theme', newTheme);
      
      console.info('[AdvancedConfig] 迁移完成: theme night -> dark');
    }
    
    // 更新版本号
    await this.put('_config_version', toVersion, false);
  }
  
  /**
   * 检查并执行迁移
   */
  async checkAndMigrate(): Promise<void> {
    const currentVersion = await this.get('_config_version', 1);
    const targetVersion = 2;  // 当前最新版本
    
    if (currentVersion < targetVersion) {
      await this.migrateConfig(currentVersion, targetVersion);
    }
  }
  
  /**
   * 获取配置描述
   */
  getConfigDescription(key: keyof AppConfig): string {
    const meta = AdvancedConfigManager.GROUPED_CONFIG.find(c => c.key === key);
    return meta?.description || '';
  }
  
  /**
   * 获取组的所有配置项
   */
  getGroupConfigs(group: ConfigGroup): GroupedConfigMeta[] {
    return AdvancedConfigManager.GROUPED_CONFIG.filter(c => c.group === group);
  }
  
  private async getDeviceId(): Promise<string> {
    return 'device_' + Date.now();
  }
}

interface ImportResult {
  success: boolean;
  imported: number;
  skipped: number;
  errors: Array<{ key: string; error: string }>;
}

四、踩坑与注意事项

4.1 配置同步延迟

问题:配置修改后,其他设备没有立即生效。

解决方案:主动同步 + UI提示

/**
 * 带同步提示的配置管理
 */
export class SyncAwareConfigManager extends AdvancedConfigManager {
  private syncStatus: 'idle' | 'syncing' | 'synced' | 'error' = 'idle';
  
  /**
   * 设置配置并等待同步
   */
  async setConfigAndWait<K extends keyof AppConfig>(
    key: K, 
    value: AppConfig[K],
    timeout: number = 5000
  ): Promise<boolean> {
    this.syncStatus = 'syncing';
    
    try {
      await this.setConfig(key, value);
      
      // 等待同步完成
      const synced = await this.waitForSync(timeout);
      
      this.syncStatus = synced ? 'synced' : 'error';
      return synced;
      
    } catch (error) {
      this.syncStatus = 'error';
      return false;
    }
  }
  
  /**
   * 等待同步完成
   */
  private async waitForSync(timeout: number): Promise<boolean> {
    return new Promise((resolve) => {
      // 简化处理,实际应监听同步事件
      setTimeout(() => resolve(true), 100);
      
      // 超时
      setTimeout(() => resolve(false), timeout);
    });
  }
  
  /**
   * 获取同步状态
   */
  getSyncStatus(): string {
    return this.syncStatus;
  }
}

4.2 配置验证失败

问题:导入的配置值不符合验证规则。

解决方案:宽松验证 + 降级处理

/**
 * 宽松验证配置管理器
 */
export class LenientConfigManager extends AdvancedConfigManager {
  /**
   * 导入配置(宽松模式)
   * 验证失败时使用默认值而不是报错
   */
  async importConfigLenient(jsonStr: string): Promise<ImportResult> {
    const result: ImportResult = {
      success: true,
      imported: 0,
      skipped: 0,
      errors: []
    };
    
    try {
      const importData = JSON.parse(jsonStr);
      
      for (const [group, config] of Object.entries(importData.config)) {
        for (const [key, value] of Object.entries(config as Record<string, any>)) {
          try {
            const meta = AdvancedConfigManager.GROUPED_CONFIG.find(c => c.key === key);
            
            if (!meta) {
              result.skipped++;
              continue;
            }
            
            // 宽松验证:失败时使用默认值
            let finalValue = value;
            if (meta.validator && !meta.validator(value)) {
              console.warn(`[LenientConfig] 验证失败,使用默认值: ${key}`);
              finalValue = meta.defaultValue;
            }
            
            await this.setConfig(key as keyof AppConfig, finalValue);
            result.imported++;
            
          } catch (error) {
            // 记录错误但继续处理
            result.errors.push({ key, error: String(error) });
          }
        }
      }
      
    } catch (error) {
      result.success = false;
      result.errors.push({ key: 'global', error: String(error) });
    }
    
    return result;
  }
}

4.3 配置冲突处理

问题:多设备同时修改同一配置。

解决方案:时间戳优先 + 用户确认

/**
 * 冲突感知配置管理器
 */
export class ConflictAwareConfigManager extends AdvancedConfigManager {
  /**
   * 设置配置(带冲突检测)
   */
  async setConfigWithConflictCheck<K extends keyof AppConfig>(
    key: K,
    value: AppConfig[K],
    expectedTimestamp?: number
  ): Promise<ConflictResult> {
    // 读取当前配置的时间戳
    const currentData = await this.getConfigWithTimestamp(key);
    
    // 检查冲突
    if (expectedTimestamp && currentData.timestamp > expectedTimestamp) {
      return {
        hasConflict: true,
        currentValue: currentData.value,
        currentTimestamp: currentData.timestamp,
        proposedValue: value
      };
    }
    
    // 无冲突,执行设置
    await this.setConfig(key, value);
    
    return {
      hasConflict: false
    };
  }
  
  /**
   * 解决冲突
   */
  async resolveConflict<K extends keyof AppConfig>(
    key: K,
    resolution: 'local' | 'remote' | 'custom',
    customValue?: AppConfig[K]
  ): Promise<void> {
    switch (resolution) {
      case 'local':
        // 保留本地值,无需操作
        break;
      case 'remote':
        // 应用远端值(需要重新获取)
        break;
      case 'custom':
        if (customValue !== undefined) {
          await this.setConfig(key, customValue);
        }
        break;
    }
  }
  
  /**
   * 获取配置带时间戳
   */
  private async getConfigWithTimestamp<K extends keyof AppConfig>(
    key: K
  ): Promise<{ value: AppConfig[K]; timestamp: number }> {
    const value = await this.getConfig(key);
    const timestamp = Date.now();  // 简化处理
    
    return { value, timestamp };
  }
}

interface ConflictResult {
  hasConflict: boolean;
  currentValue?: any;
  currentTimestamp?: number;
  proposedValue?: any;
}

五、HarmonyOS 6适配指南

5.1 API变更

5.1.1 Preferences API增强

// HarmonyOS 5.0
const prefs = await preferences.getPreferences(context, 'my_prefs');
await prefs.put('key', 'value');
await prefs.flush();

// HarmonyOS 6 - 增强的Preferences
import preferences from '@kit.ArkData';

const prefs = await preferences.getPreferences(context, 'my_prefs', {
  // HarmonyOS 6新增配置
  autoSave: true,              // 自动保存,无需手动flush
  encrypt: true,               // 加密存储
  backup: true,                // 支持备份
  distributed: true            // 支持分布式同步
});

// 直接设置,自动保存
await prefs.put('key', 'value');  // 自动flush

// 批量操作
await prefs.putBatch({
  'key1': 'value1',
  'key2': 'value2',
  'key3': 'value3'
});

5.1.2 分布式同步API

// HarmonyOS 6新增:分布式同步控制
import preferences from '@kit.ArkData';

const distributedPrefs = await preferences.getDistributedPreferences(context, 'shared_prefs', {
  // 同步配置
  syncMode: preferences.SyncMode.AUTO,      // 自动同步
  conflictStrategy: 'timestamp',             // 冲突策略
  syncScope: preferences.SyncScope.ALL_DEVICES  // 同步范围
});

// 设置同步范围
await distributedPrefs.setSyncScope(preferences.SyncScope.SPECIFIC_DEVICES, ['device1', 'device2']);

// 手动触发同步
await distributedPrefs.sync();

// 获取同步状态
const syncStatus = await distributedPrefs.getSyncStatus();
console.info(`同步状态: ${syncStatus.status}`);
console.info(`上次同步: ${syncStatus.lastSyncTime}`);

5.2 行为变更

5.2.1 自动保存

// HarmonyOS 5.0: 需要手动flush
await prefs.put('key', 'value');
await prefs.flush();  // 必须调用

// HarmonyOS 6: 自动保存
const prefs = await preferences.getPreferences(context, 'my_prefs', {
  autoSave: true  // 启用自动保存
});

await prefs.put('key', 'value');  // 自动保存,无需flush

5.3 性能优化

/**
 * HarmonyOS 6性能优化配置
 */
export class HarmonyOS6PrefsOptimization {
  /**
   * 推荐配置
   */
  getOptimizedOptions(): preferences.Options {
    return {
      autoSave: true,
      encrypt: false,  // 非敏感配置不加密,提升性能
      backup: true,
      distributed: true,
      
      // 缓存配置
      cache: {
        enabled: true,
        maxSize: 1000  // 最多缓存1000个配置项
      },
      
      // 同步配置
      sync: {
        mode: 'batch',        // 批量同步
        batchSize: 10,        // 每批10个
        delay: 1000           // 延迟1秒合并
      }
    };
  }
}

六、总结

分布式首选项让应用的个性化配置真正实现"一次设置,处处生效"。通过本文的深度解析,我们掌握了:

核心要点

  1. 架构理解:本地存储 + 分布式同步的双重机制
  2. API使用:基础操作、类型安全、分组管理、导入导出
  3. 同步策略:实时同步、延迟同步、不同步的选择
  4. 冲突处理:时间戳优先、用户确认、自定义策略
  5. 避坑指南:同步延迟、验证失败、冲突处理
  6. 版本适配:HarmonyOS 6的API增强、自动保存、性能优化

最佳实践

  • 个人偏好配置:启用同步,保证体验一致
  • 设备特定配置:禁用同步,避免干扰
  • 敏感配置:启用加密,保护隐私
  • 批量修改:使用putBatch,提升性能

配置分类建议

配置类型
├─ 外观设置(同步)
│  ├─ 主题
│  ├─ 字体大小
│  └─ 语言
├─ 通知设置(同步)
│  ├─ 通知开关
│  ├─ 声音
│  └─ 振动
├─ 隐私设置(同步)
│  ├─ 数据分析
│  └─ 崩溃报告
├─ 功能设置(同步)
│  ├─ 自动播放
│  └─ 图片质量
└─ 设备设置(不同步)
   ├─ 音量
   └─ 网络配置

掌握分布式首选项,让你的应用配置真正实现跨设备的无缝同步,为用户提供一致的使用体验。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。