news 2026/8/22 1:55:49

【Android】DataStore

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【Android】DataStore

承接ViewModel和LiveData的MVVM体系,本章我们解决轻量配置存储的现代化替换:彻底淘汰SharedPrefrernces(简称SP),用Jetpack DataStore实现记住密码、用户设置、登录状态等轻量数据持久化。

一、为什么必须淘汰SharedPreferences

SP是Android早期的轻量存储方案,在现代开发中存在多个不可忽视的硬伤,也是官方明确推荐替换的老旧API:

1. 主线程ANR风险

  • SP首次启动会全量把整个XML文件加载到内存,文件越大,阻塞主线程时间越长,容易造成页面卡顿甚至ANR(Application Not Responding(应用程序无响应))
  • commit()是同步写入,直接阻塞调用线程;apply()看似异步,但在ActivityonStop、Service销毁等生命周期节点,系统会强制等待SP写入完成,依然大概率触发ANR

2. 线程不安全

SP内部读写锁粒度粗,多线程并发读写时容易出现脏读、数据丢失,性能很差。

结合 SharedPreferences(SP)来说,“内部读写锁粒度粗”的意思是: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 核心特性

  1. 完全异步无阻塞:读写默认在 IO 线程执行,基于响应式流,不会阻塞主线程,从根源避免 SP 的 ANR 问题
  2. 线程安全原子操作:写入操作具备原子性,多线程并发安全,不会出现脏数据
  3. 天然响应式:数据变化自动推送更新,配合 LiveData 可直接驱动 UI,无需手动监听
  4. 类型安全:Key 自带类型约束,编译期就能检查类型错误
  5. 支持事务:批量修改可在一个事务内完成,保证原子性
  6. 官方迁移支持:一键从旧 SP 迁移数据,无需手动读写适配
  7. Jetpack 生态打通:和 ViewModel、Lifecycle、协程 / RxJava 无缝配合,符合现代 Android 架构

四、使用步骤

DataStore原生基于Kotlin协程 + Flow,Java环境官方提供了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 + 单例DataStore

DataStore实例必须单例,不能每个页面重复创建;建议封装在一个管理类 / Repository中,Key统一定义,避免散落各处。

创建配置存储管理类:

public class SettingsDataStore { private static volatile SettingsDataStore INSTANCE; private final RxDataStore<Preferences> dataStore; //统一定义key,自带类型,编译期类型安全 //记住的账号 public static final Preferences.Key<String> KEY_REMEMBER_ACCOUNT = PreferencesKeys.stringKey("remember_account"); //是否记住密码 public static final Preferences.Key<Boolean> 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 RxDataStore<Preferences> 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,适配MVVM)

DataStore的data()方法返回RxJava的Flowable,是一个响应式流:数据变化时自动发射最新值。我们可以把他转换成LiveData,直接在ViewModel中使用。

在Repository层封装,返回LiveData:

public class SettingsRepository { private final SettingsDataStore dataStore; public SettingsRepository(Context context) { this.dataStore = SettingsDataStore.getInstance(context); } //读取要记住的账号 public LiveData<String> 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); }); } }
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/22 1:55:13

用 Whisper 搞定离线语音转文本:Flutter 跨平台完整指南

用 Whisper 搞定离线语音转文本&#xff1a;Flutter 跨平台完整指南 【免费下载链接】whisper_dart speech recognition in dart support all audio format and support server side client side, support all language, only support in cpu only 项目地址: https://gitcod…

作者头像 李华
网站建设 2026/8/22 1:53:05

java连接linux Java秒连Linux!SSH远程控制,服务器管理爽到飞起

Java是一种用于开发应用程序、Web应用程序以及移动应用的, 具备跨平台特性的强类型语言。它是著名的Linux操作系统所普遍可用的一种语言。此外, 用以实现远程控制Linux从而让管理服务器更为简单高效的语言是Java。起先, 于Java里达成远程操纵Linux, 得动用SSH技术, 其能够达成对…

作者头像 李华
网站建设 2026/8/22 1:51:45

Python-数据类型-数字型

一、Python有六大数据类型数字&#xff08;Number&#xff09;字符串&#xff08;String&#xff09;列表&#xff08;List&#xff09;元组&#xff08;Tuple&#xff09;字典&#xff08;Dictionary&#xff09;集合&#xff08;Set&#xff09;二、而数字型又分为int&#x…

作者头像 李华
网站建设 2026/8/22 1:51:34

2026全网实测|5大主流AI论文工具排行榜!学生党闭眼入✅

随着高校查重AIGC双检全面落地&#xff0c;单纯靠通用AI写论文早已翻车率拉满。市面上各类AI论文工具、AI论文软件层出不穷&#xff0c;但功能参差不齐、合规度不一、收费套路多、AI痕迹超标等问题&#xff0c;让无数毕业生踩坑。 为了帮大家避开雷区&#xff0c;我结合2026最…

作者头像 李华
网站建设 2026/8/22 1:48:00

中小企业销售自动化实战:Rox Teams API集成与线索评分系统构建

最近在和一些中小企业的技术负责人交流时&#xff0c;发现一个普遍痛点&#xff1a;业务增长遇到瓶颈&#xff0c;市场线索获取成本越来越高&#xff0c;但内部缺乏一套系统化的工具来高效转化和管理这些线索。传统的CRM系统偏重记录&#xff0c;而市面上一些先进的销售自动化工…

作者头像 李华