尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

【Android】DataStore

【Android】DataStore 承接ViewModel和LiveData的MVVM体系本章我们解决轻量配置存储的现代化替换彻底淘汰SharedPrefrernces简称SP用Jetpack DataStore实现记住密码、用户设置、登录状态等轻量数据持久化。一、为什么必须淘汰SharedPreferencesSP是Android早期的轻量存储方案在现代开发中存在多个不可忽视的硬伤也是官方明确推荐替换的老旧API1. 主线程ANR风险SP首次启动会全量把整个XML文件加载到内存文件越大阻塞主线程时间越长容易造成页面卡顿甚至ANRApplication Not Responding应用程序无响应commit()是同步写入直接阻塞调用线程apply()看似异步但在ActivityonStop、Service销毁等生命周期节点系统会强制等待SP写入完成依然大概率触发ANR2. 线程不安全SP内部读写锁粒度粗多线程并发读写时容易出现脏读、数据丢失性能很差。结合 SharedPreferencesSP来说“内部读写锁粒度粗”的意思是SP 内部只用了一把“大铁锁”不管是读取任何键还是写入任何键所有线程都得排队抢这一把锁。3. 无类型安全所有key都是纯字符串存的是int、取的时候手滑写成getString编译不会报错运行时才会出现问题维护成本高。4. 不支持事务与原子批量修改批量修改多个配置只能多次edit()提交无法保证原子性中途失败会出现部分修改成功、部分失败的不一致状态。5. 不支持响应式数据变更后无法自动通知观察者必须手动重新读取或注册监听和MVVM的“数据驱动视图”理念完全脱节。6. 功能局限仅支持基础类型 StringSet无法便捷存储结构化对象不支持数据迁移、异常恢复文件损坏后全部失效。二、DataStore是什么 两种实现类型DataStore 是 Jetpack官方推出的新一代轻量持久化存储组件完全替代SP基于异步流式设计线程安全、类型安全天然配合LiveData / Flow完美融入MVVM架构。官方提供两种实现对应不同场景方案存储形式适用场景Preferences DataStore键值对存储和SP用法类似替代原有SP存储简单配置Proto DataStore基于Protocol Buffers存储结构化对象存储复杂、固定结构的数据强类型安全由于Proto DataStore的学习成本稍高需要定义 proto 结构因此下面所有讲解均以 Preferences DataStore 为主。三、Preferences DataStore 核心特性完全异步无阻塞读写默认在 IO 线程执行基于响应式流不会阻塞主线程从根源避免 SP 的 ANR 问题线程安全原子操作写入操作具备原子性多线程并发安全不会出现脏数据天然响应式数据变化自动推送更新配合 LiveData 可直接驱动 UI无需手动监听类型安全Key 自带类型约束编译期就能检查类型错误支持事务批量修改可在一个事务内完成保证原子性官方迁移支持一键从旧 SP 迁移数据无需手动读写适配Jetpack 生态打通和 ViewModel、Lifecycle、协程 / RxJava 无缝配合符合现代 Android 架构四、使用步骤DataStore原生基于Kotlin协程 FlowJava环境官方提供了RxJava适配版本我们可以通过RxJava LiveData转换无缝融入Java的MVVM体系。1. 添加依赖在模块级中引入依赖这里用RxJava3版本dependencies { // Preferences DataStore RxJava3 版本Java 环境专用 implementation(androidx.datastore:datastore-preferences-rxjava3:1.1.1) implementation(io.reactivex.rxjava3:rxjava:3.1.8) // 可选RxJava 转 LiveData方便 MVVM 架构使用 implementation(androidx.lifecycle:lifecycle-reactivestreams:2.6.2) }2. 统一管理key 单例DataStoreDataStore实例必须单例不能每个页面重复创建建议封装在一个管理类 / Repository中Key统一定义避免散落各处。创建配置存储管理类public class SettingsDataStore { private static volatile SettingsDataStore INSTANCE; private final RxDataStorePreferences dataStore; //统一定义key自带类型编译期类型安全 //记住的账号 public static final Preferences.KeyString KEY_REMEMBER_ACCOUNT PreferencesKeys.stringKey(remember_account); //是否记住密码 public static final Preferences.KeyBoolean KEY_REMEMBER_PASSWORD PreferencesKeys.booleanKey(remember_password); private SettingsDataStore(Context context) { //构建DataStore实例文件名自定义 dataStore new RxPreferenceDataStoreBuilder(context.getApplicationContext(), app_settings).build(); } //单例获取 public static SettingsDataStore getInstance(Context context) { if(INSTANCE null) { synchronized (SettingsDataStore.class) { if(INSTANCE null) { INSTANCE new SettingsDataStore(context); } } } return INSTANCE; } //暴露DataStore实例供读写使用 public RxDataStorePreferences getDataStore() { return dataStore; } }3. 写入数据写入通过updateDataAsync()完成是异步原子操作不会阻塞主线程// 这是一个公开方法外部调用它来保存“是否记住账号密码”的设置 public void saveRememberInfo(String account, boolean isRemember) { // 【第1步】调用 dataStore 的“异步更新”方法 // 我要修改 DataStore 里存的数据了但为了防止卡主线程请在后台线程执行。 // preferences - {} 是“回调函数”当后台线程准备好时会把“当前磁盘上的所有旧数据”传进来名字叫 preferences。 dataStore.updateDataAsync(preferences - { // 【第2步】把只读数据变成可写数据 // 传进来的 preferences 是“只读”的不允许直接修改怕你乱动原始档案。 // 所以我必须调用 toMutablePreferences()把这堆旧数据“复印”一份到一张可以随意涂改的草稿纸mutablePrefs上。 MutablePreferences mutablePrefs preferences.toMutablePreferences(); // 【第3步】在草稿纸上写第一个数据 // 在这张草稿纸上把 KEY_REMEMBER_ACCOUNT 这一栏的值改成 account。 mutablePrefs.set(KEY_REMEMBER_ACCOUNT, account); // 【第4步】在草稿纸上写第二个数据 // 在这张草稿纸上把 KEY_REMEMBER_PASSWORD 这一栏的值改成 isRemember。 mutablePrefs.set(KEY_REMEMBER_PASSWORD, isRemember); // 【第5步】把修改好的草稿纸“打包”返回 // 改完了把这张草稿纸mutablePrefs交给 DataStore。 // Single.just() 就相当于“用快递箱把草稿纸包好”告诉 DataStore“我改完了你拿着这个箱子去覆盖磁盘上的旧文件吧” // DataStore 收到后会在后台自动把整个文件原子性地一次性写入磁盘。 return Single.just(mutablePrefs); }); }4. 读取数据结合LiveData适配MVVMDataStore的data()方法返回RxJava的Flowable是一个响应式流数据变化时自动发射最新值。我们可以把他转换成LiveData直接在ViewModel中使用。在Repository层封装返回LiveDatapublic class SettingsRepository { private final SettingsDataStore dataStore; public SettingsRepository(Context context) { this.dataStore SettingsDataStore.getInstance(context); } //读取要记住的账号 public LiveDataString getRememberAccount() { return LiveDataReactiveStreams.fromPublisher( dataStore.getDataStore().data() .map(preferences - { String value preferences.get(SettingsDataStore.KEY_REMEMBER_ACCOUNT); return value ! null ? value : ; }) .onErrorReturnItem() ); } //写入方法 public void saveRememberInfo(String account, boolean isRemember) { dataStore.saveRememberInfo(account, isRemember); } }5. ViewModel中调用Activity观察ViewModel层public class SettingsViewModel extends AndroidViewModel { private final SettingsRepository repository; public SettingsViewModel(NonNull Application application) { super(application); repository new SettingsRepository(application); } }Activity层public class SettingsActivity extends AppCompatActivity { Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_settings); SettingsViewModel viewModel new ViewModelProvider(this).get(SettingsViewModel.class); Switch darkModeSwitch findViewById(R.id.switch_dark_mode); // 观察配置变化自动更新 UI viewModel.darkModeState.observe(this, isDark - { darkModeSwitch.setChecked(isDark); // 可在这里自动切换应用主题 }); // 点击开关修改配置 darkModeSwitch.setOnCheckedChangeListener((buttonView, isChecked) - { viewModel.switchDarkMode(isChecked); }); } }
返回列表