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 編寫指南

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。

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>;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章