IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> 【Android Jetpack】DataStore -> 正文阅读

[移动开发]【Android Jetpack】DataStore

1. 前言

Jetpack DataStore 是一种数据存储解决方案,允许您使用协议缓冲区存储键值对或类型化对象。DataStore使用Kotlin 协程和Flow 以异步、一致的事务方式存储数据。如果您当前在使用SharedPreferences存储数据,请考虑迁移到 DataStore。如果您需要支持大型或复杂数据集、部分更新或参照完整性,请考虑使用Room,而不是 DataStore

2. 分类

DataStore 提供两种不同的实现:Preferences DataStoreProto DataStore

  • Preferences DataStore使用键值对存储和访问数据。此实现不需要预定义的架构,也不确保类型安全。DataStore 是基于 Flow 实现的,不会阻塞主线程。只支持Int,Long,Boolean,Float,String键值对数据,适合存储简单、小型的数据,并且不支持局部更新,如果修改了其中一个值,整个文件内容将会被重新序列化。
  • Proto DataStore将数据作为自定义数据类型的实例进行存储。此实现要求您使用协议缓冲区来定义架构,但可以确保类型安全。 Proto DataStore使用协议缓冲区来定义架构。使用协议缓冲区可持久保留强类型数据。与 XML 和其他类似的数据格式相比,协议缓冲区速度更快、规格更小、使用更简单,并且更清楚明了。虽然使用 Proto DataStore 需要学习新的序列化机制,但我们认为 Proto DataStore 有着强大的优势,值得学习。

2.1 Preferences DataStore 和SharedPreferences的区别

  • SharedPreferences 有一个看上去可以在界面线程中安全调用的同步API,但是该 API 实际上执行磁盘 I/O 操作。此外,apply()会阻断fsync()上的界面线程。每次有服务启动或停止以及每次 activity 在应用中的任何地方启动或停止时,系统都会触发待处理的fsync()调用。界面线程在apply()调度的待处理fsync()调用上会被阻断,这通常会导致ANR

3. 实践

3.1 Preferences DataStore

3.1.1 依赖

// DataStore Preferences
implementation("androidx.datastore:datastore-preferences:1.0.0")
// optional - RxJava2 support
implementation("androidx.datastore:datastore-preferences-rxjava2:1.0.0")
// optional - RxJava3 support
implementation("androidx.datastore:datastore-preferences-rxjava3:1.0.0")

3.1.2 案例

下面案例来源于官网案例,地址:DataStore | Android 开发者 | Android Developers (google.cn)

class MainActivity2 : AppCompatActivity() {
    // 创建一个DataStore,并申明在顶层以方便调用
    private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "settings")

    // 声明一个int类型的key
    val EXAMPLE_COUNTER = intPreferencesKey("example_counter")
    private val textView: TextView by lazy { findViewById(R.id.text) }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }
    
    // 两个按钮的监听事件
    fun buttonOnClick(view: View){
        runBlocking {
            launch {
                when(view.id){
                    R.id.update -> {
                        incrementCounter()
                    }
                    R.id.read -> {
                        textView.text = getData().toString()
                    }
                }
            }
        }
    }

    // 存储-自增1
    suspend fun incrementCounter() {
        dataStore.edit { settings ->
            val currentCounterValue = settings[EXAMPLE_COUNTER] ?: 0
            settings[EXAMPLE_COUNTER] = currentCounterValue + 1
        }
    }

    // 获取当前值
    suspend fun getData(): Int {
        return dataStore.data.map { settings ->
            settings[EXAMPLE_COUNTER] ?: 0
        }.first()
    }
}

结果:
在这里插入图片描述

3.1.3 最后

在前面提到了,这种类型的Preferences DataStore还支持的数据类型有:Long,Boolean,Float,String等,不妨看看对应的声明函数:

longPreferencesKey()
booleanPreferencesKey()
floatPreferencesKey()
stringSetPreferencesKey()

对应的,随便找一个方法,看下是如何实现的:

@JvmName("longKey")
public fun longPreferencesKey(name: String): Preferences.Key<Long> = Preferences.Key(name)

也就是其实底层也还是使用Preferences.Key<Long>来指明类型。和SP类似数据内容也存储在本地磁盘data/data/packagename/中:
在这里插入图片描述

3.2 Proto DataStore

对于Preferences DataStore中的键只能为上述指定的类型,故而如果我们需要存储自定义对象的数据的时候,就显得力不从心了。故而在jetpack中提供了Proto DataStore

3.2.1 依赖

引入datastore的依赖:

implementation("androidx.datastore:datastore:1.0.0")
// optional - RxJava2 support
implementation("androidx.datastore:datastore-rxjava2:1.0.0")
// optional - RxJava3 support
implementation("androidx.datastore:datastore-rxjava3:1.0.0")

为了使用 Proto DataStore,让协议缓冲区为我们的架构生成代码,我们需要对 build.gradle 文件进行一些更改:

3.2.1.1 添加协议缓冲区插件

plugins {    
    ...    
    id "com.google.protobuf" version "0.8.12"
}

3.2.1.2 配置协议缓冲区

implementation  "com.google.protobuf:protobuf-javalite:3.10.0"

dependencies平级添加:

protobuf {
    protoc {
        artifact = "com.google.protobuf:protoc:3.10.0"
    }

    // Generates the java Protobuf-lite code for the Protobufs in this project. See
    // https://github.com/google/protobuf-gradle-plugin#customizing-protobuf-compilation
    // for more information.
    generateProtoTasks {
        all().each { task ->
            task.builtins {
                java {
                    option 'lite'
                }
            }
        }
    }
}

3.2.2 定义架构

app/src/main/目录下创建proto目录,然后创建一个xxx.proto文件:
在这里插入图片描述
testDemo.proto文件内容:

syntax = "proto3"; // 声明proto的版本

//  定义生成的类的包名
option java_package = "com.weizu.jetpackdemo.proto";
    
// 声明的是内部类MyProtoBean, 格式: 类型+字段名称+字段编号
message MyProtoBean {
  int32 _id = 1;
  string _name = 2;
  int32 _age = 3;
  bool _isMan = 4;
}

至于更加详细的解释可以查阅:protobuf 语言指南以及使用 Proto DataStore (google.cn)
然后Build一下,就可以看到生成的文件:
在这里插入图片描述
打开文件可以看到在该文件中生成了配置中对应的message类,和对应的set/get方法:
在这里插入图片描述
至此环境配置完毕,接着开始简单使用。

3.2.3 简单使用

3.2.3.1 创建序列化器

我们需要实现序列化器,以告知 DataStore 如何读取和写入我们在 proto 文件中定义的数据类型。如果磁盘上没有数据,序列化器还会定义默认返回值。

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

3.2.3.2 数据存储和读取

/**
 * @author 梦否 on 2022/3/28
 * @blog https://mengfou.blog.csdn.net/
 */
class MainActivity2 : AppCompatActivity() {

    private val textView: TextView by lazy { findViewById(R.id.text) }

    private val dataStore: DataStore<TestDemo.MyProtoBean> by lazy {
        DataStoreFactory.create(
            produceFile = { applicationContext.dataStoreFile("user_prefs.pb") },
            serializer = MyBeanSerializer
        )
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }

    private var i = 0

    // 按钮监听函数
    fun buttonOnClick(view: View) {
        runBlocking {
            launch {
                when (view.id) {
                    R.id.update -> {
                        storeData(1 + i, 20 + i, true, "张三${i}")
                        i++
                    }
                    R.id.read -> {
                        val first = readData().first()
                        Log.e("TAG", "id:${first.id}")
                        Log.e("TAG", "age:${first.age}")
                        Log.e("TAG", "isMan:${first.isMan}")
                        Log.e("TAG", "name:${first.name}")
                    }
                }
            }
        }
    }

    // 读取数据时处理异常
    private fun readData(): Flow<TestDemo.MyProtoBean> {
        return dataStore.data
            .catch { exception ->
                // dataStore.data throws an IOException when an error is encountered when reading data
                if (exception is IOException) {
                    Log.e("TAG", "Error reading sort order preferences.", exception)
                    emit(TestDemo.MyProtoBean.getDefaultInstance())
                } else {
                    throw exception
                }
            }
    }

    // 添加数据
    private suspend fun storeData(id: Int, age: Int, isMan: Boolean, name: String) {
        dataStore.updateData { preferences ->
            preferences.toBuilder()
                .setAge(age)
                .setId(id)
                .setIsMan(isMan)
                .setName(name)
                .build()
        }
    }
}

结果:
在这里插入图片描述

每次插入数据都会覆盖掉以前的数据,也就是在使用对象存储数据的时候,只能存储一条数据。

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-03-30 18:36:39  更:2022-03-30 18:37:01 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/25 12:36:10-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码