ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

shared_preferences_android 演进全解析:从联邦插件拆分到 Pigeon/Kotlin 双后端架构

shared_preferences_android 演进全解析:从联邦插件拆分到 Pigeon/Kotlin 双后端架构 shared_preferences_android 演进全解析从联邦插件拆分到 Pigeon/Kotlin 双后端架构【免费下载链接】packagesA collection of useful packages maintained by the Flutter team项目地址: https://gitcode.com/GitHub_Trending/pac/packages导读shared_preferences_android是 Flutter 官方插件shared_preferences在 Android 平台上的联邦federated实现负责把 Dart 层 API 落到 Android 原生的SharedPreferences与 Jetpack DataStore 之上。本文以该包的 CHANGELOG.md 为骨架结合当前仓库中真实的 Dart 实现、Kotlin 原生代码 与 构建配置梳理该包 2.0.8 → 2.4.28 的关键演进脉络帮你理解前缀过滤 API、异步双后端DataStore / SharedPreferences、JSON 化 List 编码、Pigeon 代码生成与构建体系现代化等核心机制并掌握如何在工程中正确配置与使用这些能力。一、包的定位与联邦化拆分2.0.8 起shared_preferences_android的 CHANGELOG 起点是2.0.8Split fromshared_preferencesas a federated implementation.这是整个包存在的根本原因——在 Flutter 联邦插件federated plugin体系下shared_preferences只是平台无关的门面包具体到 Android 平台的实现被拆分为独立发布的shared_preferences_android。从 pubspec.yaml 可以看到这种门面 实现的承接关系flutter: plugin: implements: shared_preferences platforms: android: package: io.flutter.plugins.sharedpreferences pluginClass: SharedPreferencesPlugin dartPluginClass: SharedPreferencesAndroid这意味着该包是**被背书endorsed**的应用只需依赖shared_preferencesAndroid 端会自动引入本包无需手动添加到pubspec.yaml见 README.md只有当你需要直接import package:shared_preferences_android/...使用其平台专属 API如异步选项类时才需要显式依赖它Dart 侧通过dartPluginClass: SharedPreferencesAndroid注册实现类原生侧通过pluginClass: SharedPreferencesPlugin注册平台插件。与平台无关层的依赖该包依赖shared_preferences_platform_interface^2.4.0SharedPreferencesAndroid继承自其中的SharedPreferencesStorePlatform对应传统同步 APISharedPreferencesAsyncAndroid继承自SharedPreferencesAsyncPlatform对应异步 API。两个实现类在 registerWith() 中一起完成注册static void registerWith() { SharedPreferencesStorePlatform.instance SharedPreferencesAndroid(); // A temporary work-around for having two plugins contained in a single package. SharedPreferencesAsyncAndroid.registerWith(); }二、从 MethodChannel 到 Pigeon 的通道演进2.0.11 → 2.1.1 → 2.4.26CHANGELOG 记录了平台通道实现方式的两次关键转变2.0.11Switches to an in-package method channel implementation.包内自建 method channel2.1.1Converts implementation to Pigeon.——首次引入 Pigeon 生成代码2.4.16升级到 Pigeon 262.4.26/2.4.27Updates internal implementation to use Kotlin Pigeon与Converts legacy backend to Kotlin连传统后端也全面 Kotlin 化。Pigeon 是 Flutter 官方的跨语言类型安全消息生成器。当前仓库中保留了完整的 Pigeon 定义源与生成产物传统 API 定义pigeons/messages.dart → 生成 Messages.g.kt 与 messages.g.dart异步 API 定义pigeons/messages_async.dart → 生成 MessagesAsync.g.kt 与 messages_async.g.dart。两个 Pigeon 定义中都显式标注了TaskQueue(type: TaskQueueType.serialBackgroundThread)这正是 CHANGELOG2.4.6所记Ensures that platform messages on background queues are handled in order的实现基础——所有读写操作在串行后台线程队列上执行避免并发写入导致的数据竞争。三、传统同步 API 与前缀过滤能力2.1.0 / 2.2.0传统 API即SharedPreferences用法对应的实现类是SharedPreferencesAndroid。CHANGELOG 中与它直接相关的功能迭代包括2.1.0新增getAllWithPrefix与clearWithPrefix2.2.0新增clearWithParameters与getAllWithParameters它们全部落到 Pigeon 的clear(prefix, allowList)与getAll(prefix, allowList)调用上见 messages.dart。在 shared_preferences_android.dart 中前缀过滤被统一抽象为PreferencesFilterstatic const String _defaultPrefix flutter.; override Futurebool clear() async { return clearWithParameters( ClearParameters(filter: PreferencesFilter(prefix: _defaultPrefix))); } override Futurebool clearWithPrefix(String prefix) async { return clearWithParameters( ClearParameters(filter: PreferencesFilter(prefix: prefix))); } override Futurebool clearWithParameters(ClearParameters parameters) async { final PreferencesFilter filter parameters.filter; return api.clear(filter.prefix, filter.allowList?.toList()); }对应地原生侧在 LegacySharedPreferencesPlugin.kt 中实现按前缀过滤override fun clear(prefix: String, allowList: ListString?): Boolean { val clearEditor preferences.edit() val allowSet allowList?.toSet() preferences.all.keys .filter { key - key.startsWith(prefix) (allowSet null || allowSet.contains(key)) } .forEach { key - clearEditor.remove(key) } return clearEditor.commit() }使用价值如果你的应用在同一个 SharedPreferences 文件中混用了多套配置例如 SDK 与业务代码共用可以通过clearWithPrefix/getAllWithPrefix只清理或只读取属于自己的前缀键避免误删他人数据。默认前缀flutter.保证了clear()只清空插件写入的数据。四、异步 API 与 DataStore / SharedPreferences 双后端2.3.0 / 2.4.0这是本包近年最重要的功能演进2.3.0新增SharedPreferencesAsyncAndroidAPI2.4.0AddsSharedPreferencessupport withinSharedPreferencesAsyncAndroidAPI——异步 API 不仅能使用默认的 Jetpack DataStore Preferences 后端还能切换到传统 AndroidSharedPreferences后端。4.1 双后端的通道设计在 shared_preferences_async_android.dart 中两个后端各自持有一条独立的 Pigeon 通道SharedPreferencesAsyncAndroid({ SharedPreferencesAsyncApi? dataStoreApi, SharedPreferencesAsyncApi? sharedPreferencesApi, }) : _dataStoreApi dataStoreApi ?? SharedPreferencesAsyncApi(messageChannelSuffix: data_store), _sharedPreferencesApi sharedPreferencesApi ?? SharedPreferencesAsyncApi(messageChannelSuffix: shared_preferences);每次调用先通过convertOptionsToPigeonOptions把 Dart 选项翻译成 Pigeon 选项再由getApiForBackend根据useDataStore标志选择后端通道SharedPreferencesPigeonOptions convertOptionsToPigeonOptions(SharedPreferencesOptions options) { if (options is SharedPreferencesAsyncAndroidOptions) { return SharedPreferencesPigeonOptions( fileName: options.originalSharedPreferencesOptions?.fileName, useDataStore: options.backend SharedPreferencesAndroidBackendLibrary.DataStore, ); } return SharedPreferencesPigeonOptions(); }4.2 两个原生后端原生侧 SharedPreferencesPlugin.kt 同时承载两个后端DataStore 后端SharedPreferencesPlugin自身实现SharedPreferencesAsyncApi通过context.sharedPreferencesDataStorepreferencesDataStore(FlutterSharedPreferences)读写写入使用runBlocking { ... .edit { } }读取使用 Flow 的firstOrNull()SharedPreferences 后端SharedPreferencesBackend类同样实现SharedPreferencesAsyncApi底层是PreferenceManager.getDefaultSharedPreferences(context)或context.getSharedPreferences(fileName, MODE_PRIVATE)见 SharedPreferencesPlugin.kt。4.3 选项类与使用示例异步 API 的 Android 专属选项定义在同文件底部enum SharedPreferencesAndroidBackendLibrary { DataStore, // 默认更新的 Jetpack DataStore Preferences SharedPreferences, // 传统 Android SharedPreferences } class SharedPreferencesAsyncAndroidOptions extends SharedPreferencesOptions { const SharedPreferencesAsyncAndroidOptions({ this.backend SharedPreferencesAndroidBackendLibrary.DataStore, this.originalSharedPreferencesOptions, }); final SharedPreferencesAndroidBackendLibrary backend; final AndroidSharedPreferencesStoreOptions? originalSharedPreferencesOptions; } class AndroidSharedPreferencesStoreOptions { const AndroidSharedPreferencesStoreOptions({this.fileName}); final String? fileName; // 存储文件名仅 SharedPreferences 后端使用 }切换后端的方式见 README.md 与 example/lib/main.dartconst SharedPreferencesAsyncAndroidOptions options SharedPreferencesAsyncAndroidOptions( backend: SharedPreferencesAndroidBackendLibrary.SharedPreferences, originalSharedPreferencesOptions: AndroidSharedPreferencesStoreOptions( fileName: the_name_of_a_file, ), );使用时把options传入异步平台 API 即可final SharedPreferencesAsyncPlatform _prefs SharedPreferencesAsyncPlatform.instance!; final int? value await _prefs.getInt(counter, options); await _prefs.setInt(counter, (value ?? 0) 1, options);选型建议依据源码行为推断默认 DataStore 后端基于协程 Flow 与类型化 Key适合新建项目当需要读取已存在的老文件历史数据、其他原生模块写入的SharedPreferences文件时应显式切换到SharedPreferences后端并可指定fileName指向目标文件。注意originalSharedPreferencesOptions仅在backend SharedPreferences时生效文档与代码注释均明确说明这一点。五、ListString 编码的两次变革JSON 化与类型安全2.3.1 → 2.4.3 → 2.4.4字符串列表的存储编码是本包历史包袱最重的部分CHANGELOG 中多次修订2.3.1修复getStringList返回不可变列表的问题2.3.4Restrict types when decoding preferences解码时限制类型2.4.3MigratesListStringvalue encoding to JSON——核心变更列表存储从原生序列化改为 JSON 编码2.4.4Restores the behavior of throwing aTypeErrorwhen callinggetStringListon a value stored withsetString——恢复对类型误用的严格报错。5.1 前缀体系strings.dart编码兼容的核心是 strings.dart 中的前缀常量Dart 与 Kotlin 两侧必须完全一致Kotlin 侧在 SharedPreferencesPlugin.kt 有注释All identifiers must match the strings.dart file常量值Base64 前缀含义listPrefixVGhpcyBpcyB0aGUgcHJlZml4IGZvciBhIGxpc3Qu平台侧序列化旧列表编码前缀jsonListPrefixVGhpcyBpcyB0aGUgcHJlZml4IGZvciBhIGxpc3Qu!JSON 列表编码前缀!不会被 Base64 产生用于区分新旧编码doublePrefixVGhpcyBpcyB0aGUgcHJlZml4IGZvciBEb3VibGUuDouble 以字符串存储时的前缀bigIntPrefixVGhpcyBpcyB0aGUgcHJlZml4IGZvciBCaWdJbnRlZ2Vy历史遗留 BigInt 前缀Kotlin 侧仍保留兼容读取5.2 JSON 写入与读取路径写入Dart 侧setValue对StringList类型执行api.setEncodedStringList(key, $jsonListPrefix${jsonEncode(value)})见 shared_preferences_android.dart异步 API 的setStringList同样拼上jsonListPrefix再jsonEncode。读取Dart 侧getAll/getPreferences遍历时凡以jsonListPrefix开头的字符串都被jsonDecode并castString()还原为ListString。读取Kotlin 侧getStringList依据前缀返回StringListResult的三态结果见 SharedPreferencesPlugin.ktoverride fun getStringList(key: String, options: SharedPreferencesPigeonOptions): StringListResult? { val stringValue getString(key, options) stringValue?.let { return if (stringValue.startsWith(JSON_LIST_PREFIX)) { StringListResult(stringValue, StringListLookupResultType.JSON_ENCODED) } else if (stringValue.startsWith(LIST_PREFIX)) { StringListResult(null, StringListLookupResultType.PLATFORM_ENCODED) } else { StringListResult(null, StringListLookupResultType.UNEXPECTED_STRING) } } return null }Dart 侧 getStringList 对三种结果分别处理jsonEncoded直接jsonDecodeplatformEncoded走getPlatformEncodedStringList兼容旧数据unexpectedString抛出TypeError。此外_convertKnownExceptions会把原生侧ClassCastException统一转换为 Dart 的TypeError——这正是 2.4.4 恢复的严格行为。版本迁移影响如果应用升级前已用旧版插件写入过字符串列表Base64 序列化格式升级到 2.4.3 后仍可读取因为platformEncoded分支保留了旧格式的兼容解码StringListObjectInputStream反序列化。但新写入的数据一律是 JSON 格式。六、原生互操作与类型细节修复2.4.9 等2.4.9是一个值得单独说明的兼容性修复Enables callers to usegetIntto read preference of typeintthat was written to shared preferences by native code without passing though plugin code.在 SharedPreferencesPlugin.kt 的SharedPreferencesBackend.getInt中可以看到具体实现——Dart 的int经 Pigeon 转为Long存储但原生代码可能直接写入 Javaintoverride fun getInt(key: String, options: SharedPreferencesPigeonOptions): Long? { val preferences createSharedPreferences(options) return if (preferences.contains(key)) { try { preferences.getLong(key, 0) } catch (e: ClassCastException) { // Retry with getInt in case the preference was written by native code directly. preferences.getInt(key, 0).toLong() } } else { null } }即getLong抛ClassCastException时自动回退到getInt并转Long让 Flutter 代码能读取原生模块写入的int类型偏好项。这与 2.4.2 之前的行为依赖插件写入的 Long形成兼容闭环。七、构建体系的现代化Groovy → Kotlin、Java 17、AGP 与 Kotlin 版本2.2.2 → 2.4.27CHANGELOG 中大量条目是构建工具链升级它们共同构成了当前 build.gradle.kts 的现状CHANGELOG 条目变更内容2.2.2minSdkVersion升至 19compileSdk 342.2.3移除 v1 Android embedding 支持2.3.2AGP 7.2.2 → 8.5.12.4.10移除 SDK 21 的过时代码2.4.14Java 兼容版本升到 172.4.15解决 Gradle 9 弃用警告2.4.17AGP 8.12.1 → 8.13.12.4.19 / 2.4.18kotlin_version 升至 2.3.0 / 2.2.212.4.21datastore 回退到 1.1.71.2.0 存在 16KB page size 回归2.4.22构建文件从 Groovy 迁移到 Kotlinbuild.gradle.kts2.4.24迁移到 Built-in Kotlin 以支持 AGP 92.4.27传统后端转换为 Kotlin当前仓库中的关键构建事实build.gradle.ktsKotlin 2.3.0、AGP 8.13.1、compileSdk flutter.compileSdkVersion不再硬编码namespace io.flutter.plugins.sharedpreferences2.1.3 引入 namespace 以兼容 AGP 8.0Java/Kotlin 目标均为 17minSdk 24高于 CHANGELOG 早期记录的 19/21当前仓库实际值以此为准依赖锁定androidx.datastore:datastore(-preferences):1.1.7、androidx.preference:preference:1.2.1lint 以warningsAsErrors true严格检查并保留 lint-baseline.xml。7.1 依赖版本锁定背后的教训2.4.20 → 2.4.21是一个典型的升级又回退案例datastore 1.1.7 → 1.2.0 后因 1.2.0 对 16KB page size 支持存在回归迅速回退到 1.1.7。这提醒使用者跟随该包升级时如果对 Android 16KB page size 或 DataStore 行为有强依赖应关注此类依赖锁定的上下文。7.2 环境与 SDK 约束当前包要求 Flutter 3.44.0、Dart SDK ^3.12.0见 pubspec.yamlCHANGELOG 中从 2.1.1 起持续记录最低 Flutter/Dart 版本的推进3.0 → 3.7/2.19 → 3.16/3.2 → 3.22/3.4 → 3.24/3.5 → 3.29/3.7 → 3.32/3.8 → 3.35/3.9 → 3.38/3.10 → 3.44/3.122.2.1 移除废弃的 splash screen meta-data2.4.8 起 compileSdk 跟随flutter.compileSdkVersion升级 Flutter 时自动对齐。八、测试与质量保障体系CHANGELOG 之外仓库提供了完整的测试矩阵来验证上述行为Dart 单测test/shared_preferences_android_test.dart传统 API与 test/shared_preferences_async_test.dart异步 API覆盖双后端与 StringList 三态解码Kotlin 原生单测LegacySharedPreferencesTest.kt 与 SharedPreferencesTest.kt使用 Robolectric 4.16、Mockito 5.2.0、MockK 1.14.11build.gradle.kts中已配置集成测试example/integration_test/shared_preferences_test.dart 与 DartIntegrationTest.kt 负责端到端验证。九、当前版本2.4.28的完整能力总结综合 CHANGELOG 全部条目与当前源码2.4.28 提供的核心能力可归纳为双 API 体系传统同步SharedPreferencesAndroid继承SharedPreferencesStorePlatform 异步SharedPreferencesAsyncAndroid继承SharedPreferencesAsyncPlatform双存储后端默认 DataStore Preferences文件FlutterSharedPreferences 可选传统SharedPreferences可指定文件名前缀与白名单过滤getAllWithPrefix/clearWithPrefix/getAllWithParameters/clearWithParameters原生侧按prefix allowList双重过滤类型安全的 List 存储JSON 编码 前缀区分新旧格式TypeError严格报错原生互操作读取原生写入的intgetLong→getInt回退、旧格式 StringSet 自动迁移为 List现代构建体系Pigeon 27.3.2 生成代码、Kotlin 2.3.0、AGP 8.13.1、Java 17、minSdk 24、严格 lint。使用建议新项目直接用默认异步 API DataStore 后端即可需要读取既有原生 SharedPreferences 文件时通过SharedPreferencesAsyncAndroidOptions(backend: SharedPreferencesAndroidBackendLibrary.SharedPreferences, originalSharedPreferencesOptions: AndroidSharedPreferencesStoreOptions(fileName: ...))切换升级版本时注意最低 Flutter/Dart 版本要求当前为 Flutter 3.44 / Dart 3.12。【免费下载链接】packagesA collection of useful packages maintained by the Flutter team项目地址: https://gitcode.com/GitHub_Trending/pac/packages创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进