dataStore 是jectpack推出的轻量化存储组件,包含PreferenceDataStore和ProtoDataStore两种方式,基于Flow()实现数据存储

Why To Use

之前的Android轻量化存储组件SharedPreferences读写操作会阻塞主线程,在数据量不多的情况下没什么影响,但随着对组件的滥用,存储的数据量逐渐增大,性能问题逐渐显现,官方也挣扎着出了apply()异步化方案,希望挽救SharedPreferences于水火,但可惜apply()并未实现真正意义上的异步,只能另立新王DataStore,两者区别如下

功能 SharePreferences PreferenceDataStore PotoDataStore
是否阻塞主线程 Y N N
是否线程安全 N Y Y
是否支持跨进程 Y N N
是否类型安全 N N Y
是否能监听数据变化 N Y Y

How To Use

PreferenceDataStore

  1. 引入依赖
implementation 'androidx.datastore:datastore-preferences:1.0.0'
  1. 初始化对象
val preDataStore:DataStore<Preferences> by preferencesDataStore(name="user")

也可以使用Context拓展函数初始化(需引入implementation 'androidx.datastore:datastore-preferences:1.0.0-alpha05' 在1.0.0版本中没有该方法,不知为啥)

private val dataStore by lazy {context.createDataStore(name = USER)}

本质都是调用PreferenceDataStoreFactory.create()方法初始化

INSTANCE = PreferenceDataStoreFactory.create(corruptionHandler = corruptionHandler,migrations = produceMigrations(applicationContext),scope = scope) {applicationContext.preferencesDataStoreFile(name)}
  1. 写入数据
//保持数据到下的key键下方suspend fun saveDataToDataStore(key:String,value:String){preDataStore.edit { it[stringPreferencesKey(key)]=value }}

由于edit方法是一个挂起函数,所以保存方法也要声明为挂起函数,在协程中调用
4. 读取数据

 //从key键取值fun getDataFromDataStore(key: String): Flow<String?> {return preDataStore.data.map { it[stringPreferencesKey(key)] }}

注意返回的是Flow类型,使用时可通过flow的collect方法获取,由于collect也是挂起函数,所以也需运行在协程中

GlobalScope.launch(Dispatchers.Main) {getDataFromDataStore("key1").collect { tvShow.setText(it) }}

使用Dispatchers线程调度设置到主线程更新UI数据,需引入依赖

implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9"

ProtoDataStore

protoDataStore 最大的特点就是类型安全,通过序列化对象存储数据

  1. 引入依赖
implementation 'androidx.datastore:datastore-core:1.0.0'
implementation 'com.google.protobuf:protobuf-java:3.14.0'
implementation 'com.google.protobuf:protoc:3.14.0'

根目录下

classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.13'

app gradle 里配置
plugins {
id ‘com.google.protobuf’
}
在android 同级配置闭包
protobuf {
protoc {
artifact = ‘com.google.protobuf:protoc:3.14.0’ // 也可以配置本地编译器路径
}

generateProtoTasks {all().each { task ->task.builtins {remove java}task.builtins {java {}// 生产java源码}}
}

}
android闭包下指定proto文件路径
sourceSets {
main {
java {
srcDir ‘src/main/java’
}
proto {
srcDir ‘src/main/proto’ //指定.proto文件路径
}
}
}

  1. 定义架构
    app/src/main/proto/目录的proto文件中定义架构,用于保存对象的类型,使用的是protobuf 语言
syntax = "proto3";option java_package = "com.example.application";
option java_multiple_files = true;message User {int32 age = 1;string userName=2;
}

主要是对message修饰的类进行定义,定义完后重构项目使proto文件生效

  1. 定义proto DataStore 对象
    3.1 实现Serializer类,T为proto文件定义的类型,重写读取和写入的方法
object UserSerializer:Serializer<User>{override val defaultValue: Userget() = User.getDefaultInstance()override suspend fun readFrom(input: InputStream): User {try {return User.parseFrom(input)}catch (e: InvalidProtocolBufferException){throw CorruptionException("Cannot read proto.", e)}}override suspend fun writeTo(t: User, output: OutputStream) {t.writeTo(output)}}

3.2 通过UserSerializer得到Proto DataStore对象:

val userDataStore:DataStore<User> by dataStore(fileName = "user.pb",serializer = UserSerializer)

本质是调用DataStoreFactory.create()方法获取对象

   DataStoreFactory.create(serializer = serializer,produceFile = { applicationContext.dataStoreFile(fileName) },corruptionHandler = corruptionHandler,migrations = produceMigrations(applicationContext),scope = scope)
  1. 设置值
suspend fun saveDataUseProto(age : Int,name:String){userDataStore.updateData { user->user.toBuilder().setAge(age).setUserName(name).build()}}

User对象中设置了age和name两个值,动态生成的User文件提供build方法设置属性值

  1. 获取值
fun getDataUseProto():Flow<User>{return userDataStore.data.map { it }}

这里it代指User对象,通过对flow操作可以获取user中的值

问题

之前使用SharedPreferences的项目如何更换到DataStore中?

在DataStore函数的创建方法中,有个参数produceMigrations,传人SharedPreferences的文件名进行初始化即可将文件内容设置到DataStore中

fun preferencesDataStore(name: String,corruptionHandler: ReplaceFileCorruptionHandler<Preferences>? = null,produceMigrations: (Context) -> List<DataMigration<Preferences>> = { listOf() },scope: CoroutineScope = CoroutineScope(Dispatchers.IO + SupervisorJob())
): ReadOnlyProperty<Context, DataStore<Preferences>> {return PreferenceDataStoreSingletonDelegate(name, corruptionHandler, produceMigrations, scope)
}

由创建函数可知produceMigrations 是一个list类型的数组,说明可以一次将sp文件全部转换完毕,转换完后原来的sp文件将会删除

val preDataStore2:DataStore<Preferences> by preferencesDataStore(name="user",produceMigrations ={listOf<DataMigration<Preferences>>(SharedPreferencesMigration(this,"user"))})

DataStore如何在同步代码中使用

使用阻塞方法获取值val exampleData = runBlocking { context.dataStore.data.first() },但如果都只是使用阻塞方式调用就可能导致UI线程卡顿,所以DataStore 又提供了预读取功能来预先加载,这样即使是阻塞调用也不会损耗太多性能

override fun onCreate(savedInstanceState: Bundle?) {lifecycleScope.launch {context.dataStore.data.first()// You should also handle IOExceptions here.}
}

实践

GitHub DataStoreDemo

参考

在Android中使用proto
一文玩转dataStore
DataStore|Android 开发者

【Jectpack】DataStore相关推荐

  1. 【Kubernetes】k8s网络概念和实操详细说明【calico网络】【含docker不同容器网络互通配置,k8s网络互通配置】【1】

    文章目录 calico网络之间通信配置[docker容器互通流程配置] calico网络原理分析 一.Calico基本介绍 二.Calico结构组成 三.Calico 工作原理 四.Calico网络方 ...

  2. 【干货】Apache Hive 2.1.1 安装配置超详细过程,配置hive、beeline、hwi、HCatalog、WebHCat等组件...

    在Docker环境成功搭建了Apache Hadoop 2.8 分布式集群,并实现了NameNode HA.ResourceManager HA之后(详见我的另一篇博文:Apache Hadoop 2 ...

  3. 【邀请函】谷歌云平台 (GCP) 入门培训:核心基础架构

    [邀请函] 谷歌云平台 (GCP) 入门培训:核心基础架构  课程介绍  这是一个为期一天的谷歌云平台 (GCP) 入门动手培训课程,将重点介绍 GCP 上核心基础架构的基础知识.通过讲师讲解.DEM ...

  4. oracle怎么搜索10条,【摘引】Oracle全文检索方面的研究(全10)

    [引用]Oracle全文检索方面的研究(全10) 4.操作实例 4.1 单列与多列支持中文检索 Create table mytable1(id number primary key, doc1 va ...

  5. 【转】UML的九种图例详解

    UML图中类之间的关系:依赖,泛化,关联,聚合,组合,实现 类与类图 1) 类(Class)封装了数据和行为,是面向对象的重要组成部分,它是具有相同属性.操作.关系的对象集合的总称. 2) 在系统中, ...

  6. 【CentOS】利用Kubeadm部署Kubernetes (K8s)

    [CentOS]利用Kubeadm部署Kubernetes (K8s)[阅读时间:约10分钟] 一.概述 二.系统环境&项目介绍 1.系统环境 2.项目的任务要求 三.具体实验流程 1 系统准 ...

  7. 【Spring】框架简介

    [Spring]框架简介 Spring是什么 Spring是分层的Java SE/EE应用full-stack轻量级开源框架,以IOC(Inverse Of Control:反转控制)和AOP(Asp ...

  8. 【C#】类——里式转换

    类是由面对对象程序设计中产生的,在面向结构的程序设计例如C语言中是没有类这个概念的!C语言中有传值调用和传址调用的两种方式!在c语言中,主方法调用方法,通过传递参数等完成一些操作,其中比较常用的的数据 ...

  9. 【C#】Out与ref是干什么的?

    关于return: 1.最后没有写 return 语句的话,表示程序正常退出 2.不需要返回值时,存在return的作用 例子 void main() {return; //return退出该程序的作 ...

  10. 【软件工程】RUP与软件开发5大模型

    软件开发的5大模型 1.瀑布模型:按照人的思维一步一步的开发下去,如果需求分析得当,每个阶段顺利,结果还不错! 2.快速原型模型:后来人们发现,自己不可能一下子就把所有的需求搞清楚,总是在开发的过程中 ...

最新文章

  1. C++读取txt文件
  2. 偶对称离散余弦变换 EDCT
  3. css垂直居中那点事
  4. 中文巨量模型“源1.0”的学习优化方法
  5. 日志进程redo thread
  6. TensorFlow2.0:常用数据范围压缩函数
  7. linux cpu 工作频率,Linux系统限制CPU工作频率(示例代码)
  8. 数据库视图作用?什么时候用视图?
  9. steam一键授权工具_Sam Haynor的100个STEAM项目
  10. 一个大三学生对杨教授博文《关于基础,不得不说》的深入
  11. 利用计算机发布调度命令时必须严格遵守,调度命令规范格式(22页)-原创力文档...
  12. PART 1.3 风控利率那些事儿(名义利率 实际利率 还款方式 以及 计算逻辑汇总)
  13. c语言正弦函数求导,正弦函数求导公式基本推导
  14. 再见Python你好C语言,再见,Python 2 你好,Python 3
  15. 云服务器如何共享文件夹,云服务器如何设置共享文件夹
  16. 德州扑克算法幕后研发者CMU博士Brown专访:AI如何打败顶级人类牌手?
  17. opencv(二)图像像素提取及操作
  18. 江苏省高校,中专校职称计算机信息技术应用能力考核,江苏省高校中专校专业技术人员职称信息技术应用能力考核资料.doc...
  19. 转载:安卓Paint使用讲解
  20. 用Python Opencv实现视频快进

热门文章

  1. 三位一体的漏洞分析方法-web应用安全测试方法
  2. 旧手机物联网_为了能让你的智能手机用十年,他们给旧手机做了一个操作系统...
  3. 微信公众号模板消息接口
  4. 【OpenGL学习笔记③】——着色器【GLSL Uniform 彩色三角形 变色正方形】
  5. python中def main是什么意思_python - 为什么要使用def main()?
  6. 混沌大学--喜茶模式拷贝指南
  7. SecureCRT 经典配色方案
  8. 炒汇中从众心理不可取
  9. ios 改变图片尺寸_iOS 修改图片尺寸的方法
  10. android图片颜色识别器,颜色识别器APP