Jetpack组件之DataStore

335 阅读1分钟

前言

目前Android简单数据存储经常使用如下三个工具:

  1. SharedPreferences
  2. MMKV
  3. DataStore

众所周知:SharedPreferences会引发ANR,所以Google官方推出了DataStore,但是国内使用MMKV居多,因为MMKV具有以下优点:

  • 简单傻瓜式使用
  • 快速内存映射快速读写(MMKV本质上实现了SharedPreferences,其底层实现使用MMAP)

但是我更加推荐使用DataStore,原因如下:

  • Google官方组件
  • 支持Kotlin协程和Flow
  • 支持跨进程

Preferences DataStore

Preferences DataStore可以读取8种类型的数据 intdoublestringbooleanfloatlongstringSetbyteArray.

初始化DataStore

val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "settings")

上面一行代码意味着,通过委托模式、双重校验懒汉单例模式通过PreferenceDataStoreFactory生成了一个DataStore对象, 在/data/data/packageName/files/datastore/目录下生成一个名为setting,后缀为preferences_pb的文件,如下所示: /data/data/edu.tyut.datastorelearn/files/datastore/settings.preferences_pb. 该文件内容如下:



content*让人

QQ_1739689797932.png

写入值

context.dataStore.edit { it: MutablePreferences ->
    val contentKey: Preferences.Key<String> =
        stringPreferencesKey(name = "content")
    it[contentKey] = content
    snackBarHostState.showSnackbar("保存成功!")
    Log.i(TAG, "Greeting -> 读取: ${it[contentKey]}")
}

读取值

val flow: Flow<String> = context.dataStore.data.map { it: Preferences -> // Preferences -> value
    val contentKey: Preferences.Key<String> =
        stringPreferencesKey(name = "content")
    it[contentKey] ?: "default"
}
coroutineScope.launch{
    flow.collectLatest{ content: String ->
        snackBarHostState.showSnackbar("content: $content")
    }
}

Proto DataStore

他的使用比较复杂。

初始化DataStore

val Context.userPreferencesStore: DataStore<Person> by dataStore(
    fileName = "person_prefs.pb",
    serializer = PersonSerializer()
)

写入值

context.userPreferencesStore.updateData { person: Person ->
    person.toBuilder().setUsername(userName)
        .setAge(age.toIntOrNull() ?: 0)
        .setMan(isMan)
        .build().apply {
            snackBarHostState.showSnackbar("保存成功Person: $this")
        }
}

读取值

val flow: Flow<Person> =
    context.userPreferencesStore.data.map { it: Person -> // Preferences -> value
        it
    }
coroutineScope.launch {
    flow.collectLatest { person: Person ->
        val username: String = String(bytes = person.username.toByteArray(charset = Charsets.UTF_8), charset = Charsets.UTF_8)
        Log.i(TAG, "Greeting -> person: $person, username: $username")
        snackBarHostState.showSnackbar("读取成功person: $person")
    }
}

Protobuf会将中文字符按照UTF-8字符集被解码并且转换为8进制数字字符串, toByteArray相当于"\350\277\231\346\230\257"(字符串) -> 0xE30xBF0x99 0xE60x980x97(数字) 然后以UTF-8编码为:这是.

完整代码示例:DataStoreLearn,文章水平有限,有什么不足还请大家指出,But代码仓库中的代码质量还是有的😊。

TODO 封装 DataStore