private lateinit var binding: ActivityMainBinding

override fun onCreate(savedInstanceState: Bundle?) {

super.onCreate(savedInstanceState)

binding = ActivityMainBinding.inflate(layoutInflater)

setContentView(binding.root)

//存数据

binding.btnPut.setOnClickListener {

}

//取数据

binding.btnGet.setOnClickListener {

}

}

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

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

//定义dataStore

private val Context.dataStore: DataStore by preferencesDataStore(name = “Study”)

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

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

//定义要操作的key

private 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,代码如下:

// 创建DataStore

val App.dataStore: DataStore 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 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 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 DataStore

implementation ‘androidx.datastore:datastore-core:1.0.0’

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)

《设计思想解读开源框架》

第一章、 热修复设计

第一节、 AOT/JIT & dexopt 与 dex2oat 第二节、 热修复设计之 CLASS_ISPREVERIFIED 问题 第三节、热修复设计之热修复原理 第四节、Tinker 的集成与使用(自动补丁包生成) 第二章、 插件化框架设计 第一节、 Class 文件与 Dex 文件的结构解读 第二节、 Android 资源加载机制详解 第三节、 四大组件调用原理 第四节、 so 文件加载机制 第五节、 Android 系统服务实现原理 第三章、 组件化框架设计 第一节、阿里巴巴开源路由框——ARouter 原理分析 第二节、APT 编译时期自动生成代码&动态类加载 第三节、 Java SPI 机制 第四节、 AOP&IOC 第五节、 手写组件化架构 第四章、图片加载框架 第一节、图片加载框架选型 第二节、Glide 原理分析 第三节、手写图片加载框架实战 第五章、网络访问框架设计 第一节、网络通信必备基础 第二节、OkHttp 源码解读 第三节、Retrofit 源码解析 第六章、 RXJava 响应式编程框架设计 第一节、链式调用 第二节、 扩展的观察者模式 第三节、事件变换设计 第四节、Scheduler 线程控制 第七章、 IOC 架构设计 第一节、 依赖注入与控制反转 第二节、ButterKnife 原理上篇、中篇、下篇 第三节、Dagger 架构设计核心解密 第八章、 Android 架构组件 Jetpack 第一节、 LiveData 原理 第二节、 Navigation 如何解决 tabLayout 问题 第三节、 ViewModel 如何感知 View 生命周期及内核原理 第四节、 Room 架构方式方法 第五节、 dataBinding 为什么能够支持 MVVM 第六节、 WorkManager 内核揭秘 第七节、 Lifecycles 生命周期 本文包含不同方向的自学编程路线、面试题集合/面经、及系列技术文章等,资源持续更新中…

、Retrofit 源码解析**

[外链图片转存中…(img-pDeMh6KP-1711908579997)]

第六章、 RXJava 响应式编程框架设计

第一节、链式调用 第二节、 扩展的观察者模式 第三节、事件变换设计 第四节、Scheduler 线程控制 [外链图片转存中…(img-HuSYe4dG-1711908579997)] 第七章、 IOC 架构设计 第一节、 依赖注入与控制反转 第二节、ButterKnife 原理上篇、中篇、下篇 第三节、Dagger 架构设计核心解密 [外链图片转存中…(img-7vJwXGPs-1711908579997)] 第八章、 Android 架构组件 Jetpack 第一节、 LiveData 原理 第二节、 Navigation 如何解决 tabLayout 问题 第三节、 ViewModel 如何感知 View 生命周期及内核原理 第四节、 Room 架构方式方法 第五节、 dataBinding 为什么能够支持 MVVM 第六节、 WorkManager 内核揭秘 第七节、 Lifecycles 生命周期 [外链图片转存中…(img-LLDJwk99-1711908579998)] 本文包含不同方向的自学编程路线、面试题集合/面经、及系列技术文章等,资源持续更新中… [外链图片转存中…(img-VQnkIrPF-1711908579998)]

本文已被CODING开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》收录

参考文章

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: