Jetpack组件 DataStore的使用和简单封装

  • 前言
  • 正文
    • 一、添加依赖
    • 二、数据存取
    • 三、数据查看和清除
    • 四、封装
    • 五、对象存取
      • 1. 插件安装
        • ① 添加协议缓冲区插件
        • ② 添加协议缓冲区和 Proto DataStore 依赖项
        • ③ 配置协议缓冲区
      • 2. 创建proto文件
      • 3. 配置proto文件
      • 4. 创建序列化器
      • 5. 对象写入和取出
    • 六、源码

前言

  也许你是第一次听说这个DataStore,也许你有所耳闻,但从未使用过,不过都没有关系,随着这篇文章去熟悉DataStore。

正文

  DataStore是Jetpack中的一个组件,用于做数据持久化,DataStore以异步、一致的事务方式存储数据,克服了SharedPreferences的一些缺点,DataStore基于Kotlin协程和Flow实现,就是用来取代SharedPreferences的。我们废话不多说,开始吧。按照惯例,我们新建一个项目去做演示,不过稍微有一些不同,这次我们新建的项目时Kotlin语言的,请注意。

创建好项目,待项目配置完成之后,我们添加依赖。

一、添加依赖

在app模块下的build.gradle中的dependencies{}闭包中添加如下依赖:

 //DataStoreimplementation 'androidx.datastore:datastore-preferences:1.0.0'implementation 'androidx.datastore:datastore-preferences-core:1.0.0'

同时添加开启ViewBinding和DataBinding,如下图所示:

然后Sync Now。

二、数据存取

  首先我们改一下activity_main.xml布局,里面的代码如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:gravity="center"android:orientation="vertical"tools:context=".MainActivity"><TextViewandroid:id="@+id/textView"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Hello World!" /><Buttonandroid:id="@+id/btn_put"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="存数据" /><Buttonandroid:id="@+id/btn_get"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="取数据" />
</LinearLayout>

里面就是两个按钮一个文本,回到MainActivity中,首先完成点击事件的监听。

 private lateinit var binding: ActivityMainBindingoverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)binding = ActivityMainBinding.inflate(layoutInflater)setContentView(binding.root)//存数据binding.btnPut.setOnClickListener {}//取数据binding.btnGet.setOnClickListener {}}

这应该没啥是好说的,就是使用了viewBinding,获取视图xml的控件id。

下面就是正式来使用DataStore了,首先我们需要定义一个变量。

 //定义dataStoreprivate val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "Study")

这里的变量就是dataStore,我们在定义的时候给了一个Study的名称,就像你使用SP时需要先给一个名字一样,然后才是键值的操作。

在DataStore中操作数据会麻烦一些,Key需要我们去定义,例如我定义一个String类型的key。

 //定义要操作的keyprivate val key = stringPreferencesKey("name")

这就是定义String类型的Key,通过这个Key去进行数据存取,还有一些其他的方法可供你使用。

基本上满足你的要求,SP的功能它肯定都会有的,这里这些方法可以快速构建一个符合类型的Key。

下面我们写一个方法进行存数据,代码如下:

 private suspend fun put() = dataStore.edit { it[key] = "疫情" }

这里用到了Kotlin的协程,如果你对这个不太了解,那么也没有关系,你先知道这么用,然后再去了解协程。这个方法这样不太清晰,换种方式:

通过dataStore.edit函数,里面的it就是MutablePreferences,然后我们通过key去设置它的值,这里是设置疫情两个字。而这个suspend是协程中的关键字,你现在可以将这个put()当成是在子线程中执行的,那么执行结束之后需要怎么做呢?需要切换到主线程。这是在调用的地方进行切换,比如我们在点击存数据按钮的时候调用,如下图所示:

就是这样的。

下面我们再写一个取数据的方法。

 private fun get() = runBlocking {return@runBlocking dataStore.data.map { it[key] ?: "新冠" }.first()}

你会发现和存数据又有不同,这里的first()就是取值,这个方法换个方式来看就清晰一些。

然后我们在取数据按钮的点击事件中调用。

下面我们运行一下:

  第一次我先取数据,显示的是默认值,然后我存数据再取数据。效果就是这样,但你会觉得使用起来很麻烦,不如SP好用,这个我们后面再去封装,先了解一些它的功能特性。

三、数据查看和清除

  在进行定义dataStore时,会在手机中生成一个pb文件,这里我们用虚拟机来看,

然后通过你的程序包名去找

这里的文件就是存放你的缓存信息的文件。这里我用txt打开看一下

可以看到键和值,也许是浏览文件不对,下面我们清理一下这个数据。在布局中增加一个按钮

在代码中

通过clear方法调用进行数据的清除,清除后我们再看看这个pb文件

这个文件就什么都没有了,清除的干干净净。

四、封装

  这个DataStore是肯定需要封装之后再使用的,直接使用太麻烦了,我们需要封装的像SP那样好用,数据类型就参考这个方法中的数据类型。

在写封装代码之前呢,我们先创建一个App类,里面的代码如下:

class App : Application() {companion object {lateinit var instance : App}override fun onCreate() {super.onCreate()instance = this}
}

然后我们在AndroidManifest中设置

下面我们新建一个EasyDataStore类,将它设置为object,先创建DataStore,代码如下:

 // 创建DataStoreval App.dataStore: DataStore<Preferences> by preferencesDataStore(name = "Study")// DataStore变量val dataStore = App.instance.dataStore

下面我们先写好各个数据类型的存取方法,先写存数据的方法:

 /*** 存放Int数据*/private suspend fun putIntData(key: String, value: Int) = dataStore.edit {it[intPreferencesKey(key)] = value}/*** 存放Long数据*/private suspend fun putLongData(key: String, value: Long) = dataStore.edit {it[longPreferencesKey(key)] = value}/*** 存放String数据*/private suspend fun putStringData(key: String, value: String) = dataStore.edit {it[stringPreferencesKey(key)] = value}/*** 存放Boolean数据*/private suspend fun putBooleanData(key: String, value: Boolean) = dataStore.edit {it[booleanPreferencesKey(key)] = value}/*** 存放Float数据*/private suspend fun putFloatData(key: String, value: Float) = dataStore.edit {it[floatPreferencesKey(key)] = value}/*** 存放Double数据*/private suspend fun putDoubleData(key: String, value: Double) = dataStore.edit {it[doublePreferencesKey(key)] = value}

然后是取数据的方法:

 /*** 取出Int数据*/private fun getIntData(key: String, default: Int = 0): Int = runBlocking {return@runBlocking dataStore.data.map {it[intPreferencesKey(key)] ?: default}.first()}/*** 取出Long数据*/private fun getLongData(key: String, default: Long = 0): Long = runBlocking {return@runBlocking dataStore.data.map {it[longPreferencesKey(key)] ?: default}.first()}/*** 取出String数据*/private fun getStringData(key: String, default: String? = null): String = runBlocking {return@runBlocking dataStore.data.map {it[stringPreferencesKey(key)] ?: default}.first()!!}/*** 取出Boolean数据*/private fun getBooleanData(key: String, default: Boolean = false): Boolean = runBlocking {return@runBlocking dataStore.data.map {it[booleanPreferencesKey(key)] ?: default}.first()}/*** 取出Float数据*/private fun getFloatData(key: String, default: Float = 0.0f): Float = runBlocking {return@runBlocking dataStore.data.map {it[floatPreferencesKey(key)] ?: default}.first()}/*** 取出Double数据*/private fun getDoubleData(key: String, default: Double = 0.00): Double = runBlocking {return@runBlocking dataStore.data.map {it[doublePreferencesKey(key)] ?: default}.first()}

最后我们根据存取的数据类型去做一个封装,存数据,代码如下:

 /*** 存数据*/fun <T> putData(key: String, value: T) {runBlocking {when (value) {is Int -> putIntData(key, value)is Long -> putLongData(key, value)is String -> putStringData(key, value)is Boolean -> putBooleanData(key, value)is Float -> putFloatData(key, value)is Double -> putDoubleData(key, value)else -> throw IllegalArgumentException("This type cannot be saved to the Data Store")}}}

取数据:

 /*** 取数据*/fun <T> getData(key: String, defaultValue: T): T {val data = when (defaultValue) {is Int -> getIntData(key, defaultValue)is Long -> getLongData(key, defaultValue)is String -> getStringData(key, defaultValue)is Boolean -> getBooleanData(key, defaultValue)is Float -> getFloatData(key, defaultValue)is Double -> getDoubleData(key, defaultValue)else -> throw IllegalArgumentException("This type cannot be saved to the Data Store")}return data as T}

对了,还有一个清除数据的方法:

 /*** 清空数据*/fun clearData() = runBlocking { dataStore.edit { it.clear() } }

这样我们的DataStore就封装好了,下面我们在MainActivity中使用一下:

这里我们存数据、取数据、清空数据都用到了,下面运行一下:

对于DataStore最基本的操作就完成了,那么下面来进阶一下。

五、对象存取

  其实我们刚才使用的是Preferences DataStore,是对数据进行操作,下面要操作的是Proto DataStore,官网上的说法是Proto DataStore 将数据作为自定义数据类型的实例进行存储。此实现要求您使用协议缓冲区来定义架构,但可以确保类型安全。

  Proto DataStore中采用的是ProtorBuffer,优势是性能好、效率高,表现在对数据的序列化和反序列化时间快,占用的空间小,还记得之前我们看到的那个pb文件吗,它里面采用的就是protobuf,之前一直是Google内部使用,这也是源于它的缺点,之前这个pb文件我们打开过,里面只能看懂键和值,缺乏描述,因此就影响了可读性,和广泛性,不如Json和XML简单。因此我们目前也只是在DataStore中使用protobuf,下面为了使用,我们需要在项目中装一个插件。

1. 插件安装

这个插件的安装比较的麻烦,首先是添加协议缓冲区插件

① 添加协议缓冲区插件

首先打开工程的build.gradle,在里面添加如下代码:

id "com.google.protobuf" version "0.8.12" apply false

再打开app下的build.gradle,添加如下代码:

 id 'com.google.protobuf'

② 添加协议缓冲区和 Proto DataStore 依赖项

在app的dependencies{}闭包中添加如下代码:

 //Proto DataStoreimplementation  'androidx.datastore:datastore-core:1.0.0'implementation  'com.google.protobuf:protobuf-javalite:3.10.0'

③ 配置协议缓冲区

在app的build.gradle中添加如下代码:

protobuf {protoc {artifact = "com.google.protobuf:protoc:3.10.0"}// 为该项目中的 Protobufs 生成 java Protobuf-lite 代码。generateProtoTasks {all().each { task ->task.builtins {java {option 'lite'}}}}
}

注意它添加的位置:

点击Sync Now。

2. 创建proto文件

将项目切换到Project,然后在main下面新建一个proto文件夹。
在此文件夹下新建study.proto文件,然后AS会发现打开这个格式需要安装一个插件。

点击Install plugins进行安装。


安装成功之后,重启AS插件生效。

注意看这个文件的图标变了,这说明你的插件安装成功并且配置成功了。

3. 配置proto文件

  里面的代码如下:

// 声明协议, 也支持 prota2,普遍使用proto3
syntax = "proto3";/*** 通过potorbuf 描述对象生成java类。*/
option java_package = "com.llw.datastore";//设置生成的类所在的包
option java_multiple_files = true;//可能会有多个文件。message PersonPreferences {string name = 1;int32 age = 2;
}

这里要按照Protobuf的语言规则去设置,参考protobuf 语言指南

这里我们定了一个对象,然后你可以Make Project,此时通过编译时技术,会生成一个PersonPreferences类,下面我们创建一个序列化器。

4. 创建序列化器

在com.llw.datastore下新建一个data包,包下新建一个PersonSerializer的单例,里面的代码如下:

object PersonSerializer : Serializer<PersonPreferences> {override val defaultValue: PersonPreferences = PersonPreferences.getDefaultInstance()override suspend fun readFrom(input: InputStream): PersonPreferences {try {return PersonPreferences.parseFrom(input)} catch (exception: InvalidProtocolBufferException) {throw CorruptionException("Cannot read proto.", exception)}}override suspend fun writeTo(t: PersonPreferences, output: OutputStream) = t.writeTo(output)
}

这里要注意导包的问题,这个类的作用就是PersonPreferences的序列化和反序列化。

5. 对象写入和取出

这里我们现在xml中增加两个按钮,如下所示:

然后回到MainActivity中,在里面添加如下代码:

 //创建 DataStoreval Context.studyDataStore: DataStore<PersonPreferences> by dataStore(fileName = "study.pb",serializer = PersonSerializer)

这里就用到了那个序列化器,然后会保存到study.pb下,下面来看存和取的方法,代码如下:

     //proto 存数据binding.btnProtoPut.setOnClickListener {runBlocking {studyDataStore.updateData {it.toBuilder().setName("刘爱国").setAge(11).build()}}}//proto 取数据binding.btnProtoGet.setOnClickListener {runBlocking {val person = studyDataStore.data.first()binding.textView.text = "name: ${person.name} , age: ${person.age}"}}

下面我们运行一下:

六、源码

GitHub:DataStoreDemo
CSDN:DataStoreDemo.rar

Android Jetpack组件 DataStore的使用和简单封装相关推荐

  1. Android Jetpack组件DataStore之Proto与Preferences存储详解与使用

    一.介绍 Jetpack DataStore 是一种数据存储解决方案,允许您使用协议缓冲区存储键值对或类型化对象.DataStore 使用 Kotlin 协程和 Flow 以异步.一致的事务方式存储数 ...

  2. Android Jetpack组件之Hilt使用

    前言 最近简单看了下google推出的框架Jetpack,感觉此框架的内容可以对平时的开发有很大的帮助,也可以解决很多开发中的问题,对代码的逻辑和UI界面实现深层解耦,打造数据驱动型UI界面. And ...

  3. Android Jetpack组件App Startup简析

    1.前言 最近简单看了下google推出的框架Jetpack,感觉此框架的内容可以对平时的开发有很大的帮助,也可以解决很多开发中的问题,对代码的逻辑和UI界面实现深层解耦,打造数据驱动型UI界面. A ...

  4. Android Jetpack组件之WorkManger使用介绍

    1.前言 最近简单看了下google推出的框架Jetpack,感觉此框架的内容可以对平时的开发有很大的帮助,也可以解决很多开发中的问题,对代码的逻辑和UI界面实现深层解耦,打造数据驱动型UI界面. A ...

  5. Android Jetpack组件之Navigation使用-源码

    1.前言 最近简单看了下google推出的框架Jetpack,感觉此框架的内容可以对平时的开发有很大的帮助,也可以解决很多开发中的问题,对代码的逻辑和UI界面实现深层解耦,打造数据驱动型UI界面. A ...

  6. Android Jetpack组件之 Room使用-源码

    1.前言 最近简单看了下google推出的框架Jetpack,感觉此框架的内容可以对平时的开发有很大的帮助,也可以解决很多开发中的问题,对代码的逻辑和UI界面实现深层解耦,打造数据驱动型UI界面. A ...

  7. Android Jetpack组件之 Paging使用-源码

    1.前言 最近简单看了下google推出的框架Jetpack,感觉此框架的内容可以对平时的开发有很大的帮助,也可以解决很多开发中的问题,对代码的逻辑和UI界面实现深层解耦,打造数据驱动型UI界面. A ...

  8. Android Jetpack组件之 LiveData使用-源码

    1.前言 最近简单看了下google推出的框架Jetpack,感觉此框架的内容可以对平时的开发有很大的帮助,也可以解决很多开发中的问题,对代码的逻辑和UI界面实现深层解耦,打造数据驱动型UI界面. A ...

  9. Android Jetpack组件之ViewModel使用

    1.前言 最近简单看了下google推出的框架Jetpack,感觉此框架的内容可以对平时的开发有很大的帮助,也可以解决很多开发中的问题,对代码的逻辑和UI界面实现深层解耦,打造数据驱动型UI界面. A ...

  10. Android Jetpack 组件之 Lifecycle源码

    1.前言 最近简单看了下google推出的框架Jetpack,感觉此框架的内容可以对平时的开发有很大的帮助,也可以解决很多开发中的问题,对代码的逻辑和UI界面实现深层解耦,打造数据驱动型UI界面. A ...

最新文章

  1. 六一:如何在Datawhale开源学习小程序中管
  2. IE弹出窗口显示URL地址栏
  3. Leetcode 剑指 Offer 04. 二维数组中的查找 (每日一题 20210727)
  4. Java 基础之 Random类和Math.random()方法
  5. pythondict增加-python字典键值对的添加和遍历方法
  6. 分布式系统理论之租约机制学习
  7. 主成分分析法_数学建模 || 葡萄酒的评价(1)主成分分析法
  8. python 3.9 发布计划_Python 3.9.0 beta4 发布
  9. WPF程序模彷Windows7的桌面任务栏
  10. Linux Jump Label/static-key机制详解
  11. 无维护地稳定运行了8 年的 Hyperic HQ
  12. 计算机多媒体技术视差估计,立体视觉中视差估计算法研究
  13. 渲染进行调用_UE渲染师Dyomin:做次世代手游,可以用好这项技能
  14. springboot controller调用service_秀!这款工具让SpringBoot不再需要Controller、Service、DAO、Mapper!...
  15. MOQL—筛选器(Selector)(一)
  16. 程序员面试金典——4.3高度最小的BST
  17. Spring Boot 2.1.3 整合 H2Datase 嵌入式数据库
  18. python如何判断用户的电话属于移动、联通、还是电信的
  19. 结构方程模型的建立、拟合、评估、筛选和结果展示全过程
  20. 四天搞懂生成对抗网络(一)——通俗理解经典GAN

热门文章

  1. 芯片破壁者(十二.下):青瓦台魔咒与半导体“死亡谷”
  2. 什么是黑盒测试,白盒测试,灰盒测试?
  3. 数据挖掘 --如何有效地进行数据挖掘和分析
  4. 用帕累托图进行数据分析
  5. 手机和电脑如何快速传大文件
  6. MySQL 安全审计、容灾备份、数据恢复
  7. Adobe PhotoShop(PS) for Mac 快捷键/PS快捷键
  8. 计算机ps移动是什么键,ps快捷键都有哪些 移动工具V等
  9. 嵌入式和单片机的区别到底在哪?
  10. Python中如何求分数化简