1.DataStore
DataStore出现之前,用的最多的存储方式就是SharedPreferences,它的使用方式简单。然而google对SP的定义为轻量级存储,如果存储的数据少,使用起来没有任何问题,当需要存储数据比较多时,SP可能会导致以下问题:
①SP第一次加载数据时需要全量加载,当数据量大时可能会阻塞UI线程造成卡顿。
②SP读写文件不是类型安全的,且没有发出错误信号的机制,缺少事务性API。
③commit() / apply()操作可能会造成ANR问题:
commit()是同步提交,会在UI主线程中直接执行IO操作,当写入操作耗时比较长时就会导致UI线程被阻塞,进而产生ANR;apply()虽然是异步提交,但异步写入磁盘时,如果执行了Activity / Service中的onStop()方法,那么一样会同步等待SP写入完毕,等待时间过长时也会引起ANR问题。
为了避免SP的这些问题,可以是使用DataStore来优化。DataStore是一种改进的新数据存储解决方案,允许使用协议缓冲区存储键值对或类型化对象。DataStore以异步、一致的事务方式存储数据,克服了SharedPreferences的一些缺点。DataStore基于Kotlin协程和Flow实现,并且可以对SP数据进行迁移,旨在取代SP。
DataStore提供了两种不同的实现:Preferences DataStore与Proto DataStore,其中Preferences DataStore用于存储键值对;Proto DataStore用于存储类型化对象。
DataStore的优势:
①DataStore基于事务方式处理数据更新。
②DataStore基于Kotlin Flow存取数据,默认在Dispatchers.IO里异步操作,避免阻塞UI线程,且在读取数据时能对发生的Exception进行处理。
③不提供apply()、commit()存留数据的方法。
④支持SP一次性自动迁移至DataStore中。
2.DataStore用法
DataStore用Kotlin语言时用法很简单,参考官网即可;对于java语言,如果不用RxJava,很难构建出DataStore对象,所以,这里使用RxJava。
①添加依赖项
implementation 'androidx.datastore:datastore-preferences:1.0.0'
implementation 'androidx.datastore:datastore-rxjava3:1.0.0'
implementation 'androidx.datastore:datastore-preferences-rxjava3:1.0.0'
②构建Preferences DataStore
RxDataStore<Preferences> dataStore = new RxPreferenceDataStoreBuilder(context, "pf_datastore").build();
这样就成功创建了Preferences DataStore,其中pf_datastore是创建Preferences DataStore的文件名称。
上述代码执行后,会在/data/data/包名/files/下创建名为pf_datastore的文件如下:
可以看到后缀名并不是xml,而是.preferences_pb。
③存数据
dataStore.updateDataAsync(prefsIn -> {
MutablePreferences mutablePreferences = prefsIn.toMutablePreferences();
MutablePreferences.set( PreferencesKeys.intKey("my_key"), 5);
return Single.just(mutablePreferences);
});
这样就将数字5存进了名为pf_datastore的文件里对应key为my_key的字段里。
④取数据
Preferences.Key<Integer> myKey = PreferencesKeys.intKey("my_key");
Flowable<Integer> myValueFlow = datastore.data().map(prefs -> prefs.get(myKey);
myValueFlow.subscribeOn(Schedulers.io()).subscribe(
@override
public void accept(Integer integer) throws Throwable {
Log.i(TAG, "取到的值为:" + integer);
}
);
通过datastore.data()
返回的是Flow数据
,后续就可以通过Flow
对数据进行一系列处理。从文件读取数据时,如果出现错误,系统会抛出IOExceptions
。可以在 map()
之前使用catch()
运算符,并且在抛出的异常是IOException
时发出emptyPreferences()
。如果出现其他类型的异常,重新抛出该异常。
注意:Preferences DataStore存取数据时的Key是Preferences.Key< T>类型,且其中的T只能存 Int、Long、Float、Double、Boolean、String、Set< String>类型。如果不是上面的类型,会直接抛异常。