使用Jetpack DataStore代替SharedPreferences

Android面试技术要点汇总
2021-05-17 14:29 · 阅读时长8分钟
小课

Jetpack DataStore是一个基于Android平台的本地数据存储解决方案,它使用Kotlin协程和Flow以异步、一致的事务方式存储数据,并且提供了两种实现,一种是Proto DataStore,使用protocol buffers协议序列化存储和访问对象,另外一种是Preferences DataStore,使用键值对的方式存储访问。它弥补了SharedPreferences的很多不足,下面是两者之间的区别:

使用Jetpack DataStore代替SharedPreferences

SharedPreferences提供了同步的API来读取数据,虽然可以在UI线程中调用,但实际上它调用了IO操作,另外apply()虽然是异步进行IO操作,但是它也有可能会阻塞UI线程,但是在Service和Activity组件的onStart和onStop生命周期方法调用时,会去等待上面的IO操作完成再继续运行,如果上面的异步IO操作没有完成的话就会造成UI线程卡顿,甚至ANR。

DataStore的两种实现,除非特殊指定,处理操作和IO操作都是在Dispatchers.IO中完成。Proto DataStore和Preference DataStore的区别如下:

  • Preference DataStore,类似SharedPreferences,无需指定序列实现,使用键值对的形式读取和存储。
  • Proto DataStore,可以直接存储复杂对象,需要指定proto序列化实现。

DataStore和Room对比

和Room比起来,DataStore更适合存储较小的数据,另外DataStore不支持部分更新,只能完整更新,如果需要支持部分更新或者数据量比较大,请选择Room,DataStore相比Room的优势是使用简单。

1、添加依赖

首先在build.gradle中添加DataStore的依赖,如果使用Datastore Preferences,依赖如下:

//Preferences DataStore 
dependencies { 
    implementation "androidx.datastore:datastore-preferences:1.0.0" 
     // 可选 - 支持RxJava2 
    implementation "androidx.datastore:datastore-preferences-rxjava2:1.0.0" 
     // 可选 - 支持RxJava3 
    implementation "androidx.datastore:datastore-preferences-rxjava3:1.0.0" 
 }  

如果是Proto DataStore,依赖如下:

// Proto DataStore (Typed API surface, such as Proto) 
dependencies { 
     implementation "androidx.datastore:datastore:1.0.0" 
     // 可选 - 支持RxJava2
     implementation "androidx.datastore:datastore-rxjava2:1.0.0" 
     // 可选 - 支持RxJava3
     implementation "androidx.datastore:datastore-rxjava3:1.0.0" 
} 
2、使用Preferences DataStore
1//使用preferencesDataStore创建的属性委托来创建DataStore
2val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "settings")
3
4//读取一个int类型数据,key是counter
5val counter = intPreferencesKey("counter")
6val counterFlow: Flow<Int> = context.dataStore.data
7  .map { preferences ->
8    preferences[counter] ?: 0
9}
10
11//写入数据,以事物方式更新数据,edit内部代码块视为单个事物
12context.dataStore.edit { preferences ->
13    val current = preferences[counter] ?: 0
14    preferences[counter] = current + 1
15}
3、使用 Proto DataStore 存储类型化的对象

在使用Proto DataStore之前,需要在app/src/main/proto/ 目录下为序列化对象定义proto文件,定义好后编译项目,编译器会自动根据proto文件生成Java类。

syntax = "proto3";

option java_package = "com.example.datastore";
option java_multiple_files = true;

message User {
   int32 id = 1;
   string name = 2;
}

然后定义好序列接口并创建DataStore进行操作

1//定义序列接口
2object UserSerializer : Serializer<User> {
3    override val defaultValue: User = User.getDefaultInstance()
4
5    override suspend fun readFrom(input: InputStream): User = User.parseFrom(input)
6
7    override suspend fun writeTo(t: User, output: OutputStream) = t.writeTo(output)
8}
9
10//创建Proto DataStore
11val Context.userDataStore: DataStore<User> by dataStore(
12    fileName = "user.pb",
13    serializer = UserSerializer
14)
15
16//读取数据
17val userFlow: Flow<User> = userDataStore.data.map { user ->
18    user
19}
20
21//写入数据
22userDataStore.updateData { current ->
23    current.toBuilder()
24        .setId(current.id + 1)
25        .setName(UUID.randomUUID().toString())
26        .build()
27}

参考:protobuf 语言指南

注释

下面是一个包含Preferences DataStore和Proto DataStore使用的简单示例。

文件大小修改时间
app
2022年02月07日
build.gradle
426 B 2022年02月07日
gradle
2022年02月07日
gradle.properties
1 kB 2022年02月07日
gradlew
6 kB 2022年02月07日
gradlew.bat
3 kB 2022年02月07日
local.properties
437 B 2022年02月07日
settings.gradle
326 B 2022年02月07日
4、从SharePreferences迁移到DataStore
  • 迁移到Preferences DataStore,只需要在创建DataStore的时候,传入以下参数,即可自动迁移SharedPreferences的数据到DataStore,其中settings_preferences是SharedPreferences中的配置文件名。
val dataStore: DataStore<Preferences> = context.createDataStore(
    name = "settings",
    migrations = listOf(SharedPreferencesMigration(context, "settings_preferences"))
)
  • 迁移到Proto DataStore,需要我们自己实现转换方法。
1val settingsDataStore: DataStore<Settings> = context.createDataStore(
2    produceFile = { File(context.filesDir, "settings.preferences_pb") },
3    serializer = SettingsSerializer,
4    migrations = listOf(
5        SharedPreferencesMigration(
6            context,
7            "settings_preferences"            
8        ) { sharedPrefs: SharedPreferencesView, currentData: UserPreferences ->
9            //实现转换方法
10          }
11    )
12)
jetpackdatastoresharedpreferencesandroid