Android使用 Jetpack DataStore 进行数据存储实例

参考Android官方文档 使用 Jetpack DataStore 进行数据存储 以及 官网介绍 Android DataStore 写了一个demo实例。

一、Preferences DataStore

1.需要kotlin的开发环境,因为DataStore内部是用协程实现的

2.使用时需添加依赖

dependencies {
  implementation "androidx.datastore:datastore-preferences:1.0.0-alpha05"
}

3.代码中使用,如下例:

package com.xxx.androidstudydemo.datastore

import android.util.Log
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.createDataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.preferencesKey
import com.hxy.androidstudydemo.DemoApp
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.runBlocking

object DataStoreHelper {
    private val TAG = "DataStoreHelper"

    //1.创建Preference DataStore
    private val dataStore: DataStore<Preferences> = DemoApp.mContext.createDataStore(
            name = "data_store_demo"
    )
    //2.定义键
    private val TEST_KEY01 = preferencesKey<String>("test_key01")
    private val TEST_KEY02 = preferencesKey<Int>("test_key02")
    private val TEST_KEY03 = preferencesKey<Boolean>("test_key03")
    //3.设置键值
    suspend fun setTestKey01(value:String ) {
        dataStore.edit { preferences -> preferences[TEST_KEY01] = value  }
    }
    suspend fun setTestKey02(value:Int ) {
        dataStore.edit { preferences -> preferences[TEST_KEY02] = value  }
    }
    suspend fun setTestKey01(value:Boolean ) {
        dataStore.edit { preferences -> preferences[TEST_KEY03] = value  }
    }
    //4.获取值
    suspend fun getTestKey01(): String {
        return dataStore.data.map { preferences -> preferences[TEST_KEY01] }.first() ?: ""
    }
    suspend fun getTestKey02(): Int {
        return dataStore.data.map { preferences -> preferences[TEST_KEY02] }.first() ?: 0
    }
    /*
    错误写法,使用collect的话Flow一直不结束,所以协程一直挂起状态,得不到值
    suspend fun getTestKey02(): Int {
        var value: Int = 0
        dataStore.data.map { preferences -> preferences[TEST_KEY02] }.collect { value = it?: 0 }
        return value
    }*/
    suspend fun getTestKey03(): Boolean {
        return dataStore.data.map { preferences -> preferences[TEST_KEY03] }.first() ?: false
    }

    fun testFun() = runBlocking {
            Log.d(TAG, "字串初始值:" + getTestKey01())
            setTestKey01("Hello world!")
            Log.d(TAG, "字串终值:" + getTestKey01())
            Log.d(TAG, "数初始值:" + getTestKey02())
            setTestKey02(666)
            Log.d(TAG, "数终值:" + getTestKey02())
            Log.d(TAG, "布尔初始值:" + getTestKey03())
            setTestKey01(true)
            Log.d(TAG, "布尔终值:" + getTestKey03())
    }
}

这里需要强调的一点就是在获取值的时候,因为是采用的Flow的机制实现的,所以如果采用collect的方式来获取值会出现Flow一直不取消,协程一直挂起等待的状态,得不到值。这里应该采用flow.first()的方式获取第一个值,然后从API介绍可知此方法会自动自动取消collect。

使用上跟SharedPreference很类似,也很简单,Android官方推荐使用,但目前还处于测试版本。

二、Proto DataStore

环境配置:基于AndroidStudio4.1.2

1.先AS中安装一个插件Protocol Buffer Editor,方便proto文件的编写和格式化

2.配置proto的编译环境:

a)工程目录build.gradle中添加插件 

dependencies {
    classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.14'
}

b)模块目录build.gradle中加入如下内容:原有内容以“...”替代,对照层级加入相应配置

plugins {
.......
    id 'com.google.protobuf'
}

android {
........
    sourceSets {
        main {
            proto {
                srcDir 'src/main/proto'
                include '**/*.proto'
            }
            java {
                srcDir 'src/main/java'
            }
        }
    }
}

dependencies {
..........
    implementation "androidx.datastore:datastore:1.0.0-alpha05"
    implementation "com.google.protobuf:protobuf-javalite:3.14.0"
}

protobuf {
    protoc {
        artifact = "com.google.protobuf:protoc:3.14.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.编写proto文件,路径为上述配置中的src/main/proto下,这个可以修改,命名为xxx.proto,并重新rebuild project,其会在build->generated/source/下生成此proto相关的java代码

syntax = "proto3";

option java_package = "com.hxy.androidstudydemo.datastore";
option java_multiple_files = true;

message TestData {
  int32 test_id = 1;
  string msg = 2;
  bool enable = 3;
}

4.将生成的与proto相匹配的java类序列化

object TestDataSerializer : Serializer<TestData> {//TestData即为编译生成的java类
    override fun readFrom(input: InputStream): TestData {
        return TestData.parseFrom(input)//序列化过程只需简单的对应一下
    }

    override fun writeTo(t: TestData, output: OutputStream) {
        t.writeTo(output)
    }

    override val defaultValue: TestData
        get() = TestData.getDefaultInstance()
}

5.使用4中的序列化文件创建Proto DataStore实例,如下代码;

class TestProtoDataStore {
    private val dataStore by lazy {
        DemoApp.mContext.createDataStore(
            fileName = FILENAME,
            serializer = TestDataSerializer//上一步序列化后的类
        )
    }

    suspend fun setTestDataId(id: Int) {
        dataStore.updateData {
            it.toBuilder().setTestId(id).build()
        }
    }

    suspend fun getTestDataId(): Int {
        return dataStore.data.map { it.testId }.first()
    }

    suspend fun setTestDataMsg(msg: String) {
        dataStore.updateData {
            it.toBuilder().setMsg(msg).build()
        }
    }

    suspend fun getTestDataMsg(): String {
        return dataStore.data.map { it.msg }.first()
    }

    suspend fun setTestDataEnable(bool: Boolean) {
        dataStore.updateData {
            it.toBuilder().setEnable(bool).build()
        }
    }

    suspend fun isTestDataEnable(): Boolean {
        return dataStore.data.map { it.enable }.first()
    }

    suspend fun setTestData(data: TestData) {
        dataStore.updateData {
            it.toBuilder()
                .setTestId(data.testId)
                .setEnable(data.enable)
                .setMsg(data.msg)
                .build()
        }
    }

    suspend fun getTestData(): TestData {
        return dataStore.data.first()
    }

    companion object{
        const val FILENAME = "test.pb"
        fun testFun() = runBlocking {
            val testData = TestData.getDefaultInstance()
        }
    }
}

接下来就可以像一般类和方法一样使用了。

注意:Preference DataStore和Proto DataStore引用的jar包有区别,如果工程中同时使用了二者,需要将两个包都加进去;请注意到:Preference DataStore创建时使用的是

androidx.datastore.preferences.PreferenceDataStore.Context.createDataStore(name="xxxx")
而Proto DataStore是
androidx.datastore.DataStoreFactory.Context.createDataStore(fileName="xxx.pb",serializer = XXX)

 

源码:CODE.CHINA   Android-studydemo


为什么使用DataStore?

官方给了一张图应该能介绍清楚了:

参考文档:

ProtoBuffer官网 https://developers.google.cn/protocol-buffers

官网介绍 https://developer.android.google.cn/topic/libraries/architecture/datastore#typed-datastore

Android Jetpack 之 DataStore https://blog.youkuaiyun.com/zzw0221/article/details/109274610

GitHub上DataStoreSample https://github.com/danledian/DataStoreSample

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

潇潇独行侠

如果有帮助到您,可以请杯快乐水

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值