1 添加依赖
2 在 main 文件夹下创建 .proto 文件
3 编写 Serializer
4 编写基础存储工具类
5 举例使用
** 5.1 创建 DataStore 存储工具子类
** 5.2 基本使用
6 混淆
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。
ds
3 编写 Serializer
object MessageEventSerializer :Serializer{
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 {
;
}
*/
open class DataStoreBase(name: String) {
val dataStore: DataStore? by lazy {
AppApplication.context?.createDataStore(name)
}
val messageEventDataStore: DataStore? 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? = null): Set? {
return dataStore?.data?.map { preferences ->
preferences[stringSetPreferencesKey(key)] ?: defaultValue
}?.first()
}
suspend fun getObject(key: String, clazz: Class, 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 get(key: String, clazz: KClass, 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
}
}
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 {
;
}