Android 使用DataStore存储数据

  • 1 添加依赖
  • 2 在 main 文件夹下创建 .proto 文件
  • 3 编写 Serializer
  • 4 编写基础存储工具类
  • 5 举例使用
    ** 5.1 创建 DataStore 存储工具子类
    ** 5.2 基本使用
  • 6 混淆

0 使用 SharedPreferences 保存数据

0.1 创建SP Base 类

open class BaseSp(val name: String = "Clear") {


    private var sp: SharedPreferences? =
        AppApplication.context?.getSharedPreferences(name, Context.MODE_PRIVATE)

    fun setValue(key: String, any: Any) {
        sp?.let {
            val edit = it.edit()
            when (any) {
                is String -> edit.putString(key, any)
                is Boolean -> edit.putBoolean(key, any)
                is Int -> edit.putInt(key, any)
                is Long -> edit.putLong(key, any)
                is Float -> edit.putFloat(key, any)
                else -> {
                    edit.putString(key, any.toString())
                }
            }
            edit.apply()
        }
    }

    fun getBooleanValue(key: String, default: Boolean): Boolean {
        return sp?.getBoolean(key, default) ?: default
    }

    fun getStringValue(key: String, default: String? = ""): String? {
        return sp?.getString(key, default) ?: default
    }

    fun getIntValue(key: String, default: Int): Int {
        return sp?.getInt(key, default) ?: default
    }

    fun getLongValue(key: String, default: Long): Long {
        return sp?.getLong(key, default) ?: default
    }

    fun getFloatValue(key: String, default: Float): Float {
        return sp?.getFloat(key, default) ?: default
    }


    fun clearAll() {
        sp?.edit()?.clear()?.apply()
    }

}

0.2 继承SP Base 实现自己的 sp 文件

object MySp : BaseSp("可选sp名") {
      ...
}

1 添加依赖

    implementation "androidx.datastore:datastore:1.0.0-alpha06"
    implementation "androidx.datastore:datastore-preferences:1.0.0-alpha06"
    implementation "com.google.protobuf:protobuf-java:3.15.0"

2 在 main 文件夹下创建 .proto文件

Proto 编写指南

.proto

syntax = "proto3";

option java_package = "com.afra55.xxx.ds.event";
option java_multiple_files = true;

message MessageEvent {
  int32 type = 1;
  string message = 2;
}

根据不同的存储需求来实现不同的 proto 文件,然后进行编译, java_package 是生成文件的路径
protoc -I=$SRC_DIR --java_out=$DST_DIR $SRC_DIR/ds.proto

例如当在 proto 文件夹下:

protoc -I=. --java_out=../java ./ds.proto

许多标准的简单数据类型都可以作为字段类型,包括bool,int32,float,double,和string。

ds

protoc 命令下载地址:
https://github.com/protocolbuffers/protobuf/releases

3 编写 Serializer

object MessageEventSerializer :Serializer<MessageEvent>{

    override val defaultValue: MessageEvent
        get() = MessageEvent.getDefaultInstance()

    override fun readFrom(input: InputStream): MessageEvent {

        try {
            return MessageEvent.parseFrom(input)
        } catch (e: Exception) {
            throw CorruptionException("Cannot read proto.", e)
        }
    }

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

}

4 编写基础存储工具类


import androidx.datastore.core.DataStore
import androidx.datastore.createDataStore
import androidx.datastore.preferences.core.*
import androidx.datastore.preferences.createDataStore
import androidx.datastore.createDataStore
import com.afra55.thunderomadtestapplication.AppApplication
import com.afra55.thunderomadtestapplication.ds.event.MessageEvent
import com.afra55.thunderomadtestapplication.toJsonString
import com.afra55.thunderomadtestapplication.toObject
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlin.reflect.KClass

/**
 * @author Afra55
 * @date 2/18/21
 * A smile is the best business card.
 *
 * 需要添加混淆:
-keepclassmembers class * extends androidx.datastore.preferences.protobuf.GeneratedMessageLite {
<fields>;
}
 */
open class DataStoreBase(name: String) {
    val dataStore: DataStore<Preferences>? by lazy {
        AppApplication.context?.createDataStore(name)
    }

    val messageEventDataStore: DataStore<MessageEvent>? by lazy {
        AppApplication.context?.createDataStore(
            fileName = name,
            serializer = MessageEventSerializer
        )
    }


    suspend fun getString(key: String, defaultValue:String? = null):String? {
       return dataStore?.data?.map { preferences ->
            preferences[stringPreferencesKey(key)] ?: defaultValue
        }?.first()
    }

    suspend fun getInt(key: String, defaultValue:Int? = null): Int? {
       return dataStore?.data?.map { preferences ->
            preferences[intPreferencesKey(key)] ?: defaultValue
        }?.first()
    }

    suspend fun getDouble(key: String, defaultValue:Double? = null): Double? {
       return dataStore?.data?.map { preferences ->
            preferences[doublePreferencesKey(key)] ?: defaultValue
        }?.first()
    }
    suspend fun getBoolean(key: String, defaultValue:Boolean? = null): Boolean? {
       return dataStore?.data?.map { preferences ->
            preferences[booleanPreferencesKey(key)] ?: defaultValue
        }?.first()
    }

    suspend fun getFloat(key: String, defaultValue:Float? = null): Float? {
        return dataStore?.data?.map { preferences ->
            preferences[floatPreferencesKey(key)] ?: defaultValue
        }?.first()
    }
    suspend fun getLong(key: String, defaultValue:Long? = null): Long? {
       return dataStore?.data?.map { preferences ->
            preferences[longPreferencesKey(key)] ?: defaultValue
        }?.first()
    }
    suspend fun getStringSet(key: String, defaultValue:Set<String>? = null): Set<String>? {
       return dataStore?.data?.map { preferences ->
            preferences[stringSetPreferencesKey(key)] ?: defaultValue
        }?.first()
    }
    suspend fun <T> getObject(key: String, clazz: Class<T>, defaultValue:T? = null): T? {
       return dataStore?.data?.map { preferences ->
           val a = preferences[stringPreferencesKey(key)]
           if (a != null) {
               a.toObject(clazz) as T
           } else {
               defaultValue
           }
        }?.first()
    }
    suspend fun <T : Any> get(key: String, clazz: KClass<T>, defaultValue: T? = null): T? {

        val pKey = when (clazz) {
            String::class -> {
                stringPreferencesKey(key)
            }
            Int::class -> {
                intPreferencesKey(key)
            }
            Double::class -> {
                doublePreferencesKey(key)
            }
            Boolean::class -> {
                booleanPreferencesKey(key)
            }
            Float::class -> {
                floatPreferencesKey(key)
            }
            Long::class -> {
                longPreferencesKey(key)
            }
            Set::class -> {
                stringSetPreferencesKey(key)
            }
            else -> {
                null
            }
        }
        return dataStore?.data?.map { preferences ->
            if (pKey != null) {
                (preferences[pKey] as? T) ?: defaultValue
            } else {
                val a = preferences[stringPreferencesKey(key)]
                if (a != null) {
                    a.toObject(clazz::class.java) as T
                } else {
                    defaultValue
                }
            }
        }?.first()
    }

    suspend fun put(key: String, value: Any) {
        when (value::class) {
            String::class -> {
                dataStore?.edit { settings ->
                    settings[stringPreferencesKey(key)] = value as String
                }
            }
            Int::class -> {
                dataStore?.edit { settings ->
                    settings[intPreferencesKey(key)] = value as Int
                }
            }
            Double::class -> {
                dataStore?.edit { settings ->
                    settings[doublePreferencesKey(key)] = value as Double
                }
            }
            Boolean::class -> {
                dataStore?.edit { settings ->
                    settings[booleanPreferencesKey(key)] = value as Boolean
                }
            }
            Float::class -> {
                dataStore?.edit { settings ->
                    settings[floatPreferencesKey(key)] = value as Float
                }
            }
            Long::class -> {
                dataStore?.edit { settings ->
                    settings[longPreferencesKey(key)] = value as Long
                }
            }
            Set::class -> {
                dataStore?.edit { settings ->
                    settings[stringSetPreferencesKey(key)] = value as Set<String>
                }
            }
            else -> {
                dataStore?.edit { settings ->
                    settings[stringPreferencesKey(key)] = value.toJsonString()
                }
            }
        }
    }


    suspend fun getMessageEventType():Int?  {
        return messageEventDataStore?.data?.map {
            it.type
        }?.first()
    }
    suspend fun getMessageEventData():String? {
        return messageEventDataStore?.data?.map {
            it.message
        }?.first()
    }
    suspend fun getMessageEvent():MessageEvent? {
        return messageEventDataStore?.data?.map {
            it
        }?.first()
    }

    suspend fun putMessageEvent(message:String) {
        messageEventDataStore?.updateData {
            it.toBuilder()
                .setType(it.type)
                .setMessage(message)
                .build()
        }
    }

    suspend fun putMessageEvent(type:Int) {
        messageEventDataStore?.updateData {
            it.toBuilder()
                .setType(type)
                .setMessage(it.message)
                .build()
        }
    }

    suspend fun putMessageEvent(type:Int, message:String) {
        messageEventDataStore?.updateData {
            it.toBuilder()
                .setType(type)
                .setMessage(message)
                .build()
        }
    }
}

5 举例使用

5.1 创建 DataStore 存储工具子类

object IPDataStore :DataStoreBase("ip"){

}

5.2 基本使用

  lifecycleScope.launch {
            Log.i("ip", IPDataStore.getString("ip")?:"null")
            Log.i("message_event", IPDataStore.getMessageEventType().toString())
            Log.i("message_event", IPDataStore.getMessageEventData().toString())
            val r = withContext(Dispatchers.IO){
                try {
                    runHttp("http://ip-api.com/json")
                } catch (e: Exception) {
                    e.printStackTrace()
                }
            }

            if (r is String) {
                IPDataStore.put("ip", r)
                IPDataStore.putMessageEvent(12, r)
                IPDataStore.getString("ip")?.let {
                    Log.i("ip", it?:"null")
                }

            }
            Log.i("message_event", IPDataStore.getMessageEventType().toString())
            Log.i("message_event", IPDataStore.getMessageEventData().toString())

            IPDataStore.getMessageEvent()?.let {
                Log.i("message_event", it.toString())
            }
            Log.i("ip", "获取完成")
        }
    var client = OkHttpClient()

    fun runHttp(url: String): String? {
        val request: Request = Request.Builder()
            .url(url)
            .build()
        client.newCall(request).execute().use { response -> return response.body!!.string() }
    }

6 混淆

-keepclassmembers class * extends androidx.datastore.preferences.protobuf.GeneratedMessageLite {
    <fields>;
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,002评论 6 509
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,777评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,341评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,085评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,110评论 6 395
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,868评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,528评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,422评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,938评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,067评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,199评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,877评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,540评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,079评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,192评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,514评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,190评论 2 357

推荐阅读更多精彩内容