使用 Hilt 提供首选项数据存储
Provide preferences datastore with Hilt
我试图提供一个通用的 DataStore<Preferences>
,以便可以在多个地方使用相同的首选项文件,但我收到了有用的错误消息:
Cannot find symbol: DaggerMyApplication_HiltComponents_SingletonC.builder()
@Module
@InstallIn(ApplicationComponent::class)
object DataStoreModule {
@Provides
fun provideDataStore(@ApplicationContext context: Context): DataStore<Preferences> = context.createDataStore("settings")
}
但是我可以执行以下操作并在 @Inject
构造函数中使用它。
@Singleton
class DataStoreProvider @Inject constructor(@ApplicationContext context: Context) {
val dataStore: DataStore<Preferences> = context.createDataStore("settings")
}
我假设扩展程序 createDataStore
正在做一些 Hilt 不喜欢的事情,但即使问题无法解决,我也希望能解释发生了什么。
这对我有用:
@Provides
@Singleton
fun dataStore(@ApplicationContext appContext: Context): DataStore<Preferences> =
appContext.createDataStore("settings")
想法是将 @Singleton
置于提供者方法之后。
2021 年 2 月 9 日更新:
最好创建一个 manager 并提供:
class DataStoreManager(appContext: Context) {
private val settingsDataStore = appContext.createDataStore("settings")
suspend fun setThemeMode(mode: Int) {
settingsDataStore.edit { settings ->
settings[Settings.NIGHT_MODE] = mode
}
}
val themeMode: Flow<Int> = settingsDataStore.data.map { preferences ->
preferences[Settings.NIGHT_MODE] ?: AppCompatDelegate.MODE_NIGHT_UNSPECIFIED
}
}
@InstallIn(SingletonComponent::class)
@Module
class AppModule {
@Provides
@Singleton
fun dataStoreManager(@ApplicationContext appContext: Context): DataStoreManager =
DataStoreManager(appContext)
2021 年 3 月 20 日更新:
版本 1.0.0-alpha07
private val Context.dataStore by preferencesDataStore("settings")
class DataStoreManager(appContext: Context) {
private val settingsDataStore = appContext.dataStore
suspend fun setThemeMode(mode: Int) {
settingsDataStore.edit { settings ->
settings[Settings.NIGHT_MODE] = mode
}
}
val themeMode: Flow<Int> = settingsDataStore.data.map { preferences ->
preferences[Settings.NIGHT_MODE] ?: AppCompatDelegate.MODE_NIGHT_UNSPECIFIED
}
}
2021 年 5 月 1 日更新:
@Florian 完全正确,我忘记了。
删除dataStoreManager
提供商。那么,
private val Context.dataStore by preferencesDataStore("settings")
@Singleton //You can ignore this annotation as return `datastore` from `preferencesDataStore` is singletone
class DataStoreManager @Inject constructor(@ApplicationContext appContext: Context) {
private val settingsDataStore = appContext.dataStore
suspend fun setThemeMode(mode: Int) {
settingsDataStore.edit { settings ->
settings[Settings.NIGHT_MODE] = mode
}
}
val themeMode: Flow<Int> = settingsDataStore.data.map { preferences ->
preferences[Settings.NIGHT_MODE] ?: AppCompatDelegate.MODE_NIGHT_UNSPECIFIED
}
}
如 Dr.jacky 所述,现在推荐使用创建管理器的方式,但您仍然可以使用 PreferenceDataStoreFactory
并创建 Preferences DataStore 单例:
@Provides
@Singleton
fun providePreferencesDataStore(@ApplicationContext appContext: Context): DataStore<Preferences> =
PreferenceDataStoreFactory.create(
produceFile = {
appContext.preferencesDataStoreFile(PREFERENCES_STORE_NAME)
}
)
我使用 DataStore<Preferences>
和 Hilt 如下。
PersistenceModule.kt
@Module
@InstallIn(SingletonComponent::class)
object PersistenceModule {
@Provides
@Singleton
fun provideDataStoreManager(@ApplicationContext context: Context): DataStoreManager {
return DataStoreManager(context)
}
}
DataStoreManager.kt
class DataStoreManager @Inject constructor(@ApplicationContext private val context: Context) {
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(STORE_NAME)
private suspend fun <T> DataStore<Preferences>.getFromLocalStorage(
PreferencesKey: Preferences.Key<T>, func: T.() -> Unit) {
data.catch {
if (it is IOException) {
emit(emptyPreferences())
} else {
throw it
}
}.map {
it[PreferencesKey]
}.collect {
it?.let { func.invoke(it as T) }
}
}
suspend fun <T> storeValue(key: Preferences.Key<T>, value: T) {
context.dataStore.edit {
it[key] = value
}
}
suspend fun <T> readValue(key: Preferences.Key<T>, responseFunc: T.() -> Unit) {
context.dataStore.getFromLocalStorage(key) {
responseFunc.invoke(this)
}
}
}
ViewModel.kt
@HiltViewModel
class HomeViewModel @Inject constructor(
private val dataStore: DataStoreManager
) : LiveCoroutinesViewModel() {
fun readNextReviewTime() {
viewModelScope.launch {
dataStore.readValue(nextReviewTime) {
// Here you can do something with value.
}
}
}
}
更新
@HiltViewModel
class TranslateViewModel @Inject constructor(
definitionRepository: DefinitionRepository,
translateRepository: TranslateRepository,
val dataStoreManager: DataStoreManager
) : LiveCoroutinesViewModel() {
init {
readValueInViewModelScope(sourceLanguage, "ta") { // use value here }
readValueInViewModelScope(targetLanguage, "si") { // use value here }
}
private fun <T> readValueInViewModelScope(key: Preferences.Key<T>, defaultValue: T, onCompleted: T.() -> Unit) {
viewModelScope.launch {
dataStoreManager.readValue(key) {
if (this == null) {
storeValueInViewModelScope(key, defaultValue)
} else {
onCompleted.invoke(this)
}
}
}
}
fun <T> storeValueInViewModelScope(key: Preferences.Key<T>, value: T) {
viewModelScope.launch {
dataStoreManager.storeValue(key, value)
}
}
}
我试图提供一个通用的 DataStore<Preferences>
,以便可以在多个地方使用相同的首选项文件,但我收到了有用的错误消息:
Cannot find symbol: DaggerMyApplication_HiltComponents_SingletonC.builder()
@Module
@InstallIn(ApplicationComponent::class)
object DataStoreModule {
@Provides
fun provideDataStore(@ApplicationContext context: Context): DataStore<Preferences> = context.createDataStore("settings")
}
但是我可以执行以下操作并在 @Inject
构造函数中使用它。
@Singleton
class DataStoreProvider @Inject constructor(@ApplicationContext context: Context) {
val dataStore: DataStore<Preferences> = context.createDataStore("settings")
}
我假设扩展程序 createDataStore
正在做一些 Hilt 不喜欢的事情,但即使问题无法解决,我也希望能解释发生了什么。
这对我有用:
@Provides
@Singleton
fun dataStore(@ApplicationContext appContext: Context): DataStore<Preferences> =
appContext.createDataStore("settings")
想法是将 @Singleton
置于提供者方法之后。
2021 年 2 月 9 日更新:
最好创建一个 manager 并提供:
class DataStoreManager(appContext: Context) {
private val settingsDataStore = appContext.createDataStore("settings")
suspend fun setThemeMode(mode: Int) {
settingsDataStore.edit { settings ->
settings[Settings.NIGHT_MODE] = mode
}
}
val themeMode: Flow<Int> = settingsDataStore.data.map { preferences ->
preferences[Settings.NIGHT_MODE] ?: AppCompatDelegate.MODE_NIGHT_UNSPECIFIED
}
}
@InstallIn(SingletonComponent::class)
@Module
class AppModule {
@Provides
@Singleton
fun dataStoreManager(@ApplicationContext appContext: Context): DataStoreManager =
DataStoreManager(appContext)
2021 年 3 月 20 日更新:
版本 1.0.0-alpha07
private val Context.dataStore by preferencesDataStore("settings")
class DataStoreManager(appContext: Context) {
private val settingsDataStore = appContext.dataStore
suspend fun setThemeMode(mode: Int) {
settingsDataStore.edit { settings ->
settings[Settings.NIGHT_MODE] = mode
}
}
val themeMode: Flow<Int> = settingsDataStore.data.map { preferences ->
preferences[Settings.NIGHT_MODE] ?: AppCompatDelegate.MODE_NIGHT_UNSPECIFIED
}
}
2021 年 5 月 1 日更新: @Florian 完全正确,我忘记了。
删除dataStoreManager
提供商。那么,
private val Context.dataStore by preferencesDataStore("settings")
@Singleton //You can ignore this annotation as return `datastore` from `preferencesDataStore` is singletone
class DataStoreManager @Inject constructor(@ApplicationContext appContext: Context) {
private val settingsDataStore = appContext.dataStore
suspend fun setThemeMode(mode: Int) {
settingsDataStore.edit { settings ->
settings[Settings.NIGHT_MODE] = mode
}
}
val themeMode: Flow<Int> = settingsDataStore.data.map { preferences ->
preferences[Settings.NIGHT_MODE] ?: AppCompatDelegate.MODE_NIGHT_UNSPECIFIED
}
}
如 Dr.jacky 所述,现在推荐使用创建管理器的方式,但您仍然可以使用 PreferenceDataStoreFactory
并创建 Preferences DataStore 单例:
@Provides
@Singleton
fun providePreferencesDataStore(@ApplicationContext appContext: Context): DataStore<Preferences> =
PreferenceDataStoreFactory.create(
produceFile = {
appContext.preferencesDataStoreFile(PREFERENCES_STORE_NAME)
}
)
我使用 DataStore<Preferences>
和 Hilt 如下。
PersistenceModule.kt
@Module
@InstallIn(SingletonComponent::class)
object PersistenceModule {
@Provides
@Singleton
fun provideDataStoreManager(@ApplicationContext context: Context): DataStoreManager {
return DataStoreManager(context)
}
}
DataStoreManager.kt
class DataStoreManager @Inject constructor(@ApplicationContext private val context: Context) {
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(STORE_NAME)
private suspend fun <T> DataStore<Preferences>.getFromLocalStorage(
PreferencesKey: Preferences.Key<T>, func: T.() -> Unit) {
data.catch {
if (it is IOException) {
emit(emptyPreferences())
} else {
throw it
}
}.map {
it[PreferencesKey]
}.collect {
it?.let { func.invoke(it as T) }
}
}
suspend fun <T> storeValue(key: Preferences.Key<T>, value: T) {
context.dataStore.edit {
it[key] = value
}
}
suspend fun <T> readValue(key: Preferences.Key<T>, responseFunc: T.() -> Unit) {
context.dataStore.getFromLocalStorage(key) {
responseFunc.invoke(this)
}
}
}
ViewModel.kt
@HiltViewModel
class HomeViewModel @Inject constructor(
private val dataStore: DataStoreManager
) : LiveCoroutinesViewModel() {
fun readNextReviewTime() {
viewModelScope.launch {
dataStore.readValue(nextReviewTime) {
// Here you can do something with value.
}
}
}
}
更新
@HiltViewModel
class TranslateViewModel @Inject constructor(
definitionRepository: DefinitionRepository,
translateRepository: TranslateRepository,
val dataStoreManager: DataStoreManager
) : LiveCoroutinesViewModel() {
init {
readValueInViewModelScope(sourceLanguage, "ta") { // use value here }
readValueInViewModelScope(targetLanguage, "si") { // use value here }
}
private fun <T> readValueInViewModelScope(key: Preferences.Key<T>, defaultValue: T, onCompleted: T.() -> Unit) {
viewModelScope.launch {
dataStoreManager.readValue(key) {
if (this == null) {
storeValueInViewModelScope(key, defaultValue)
} else {
onCompleted.invoke(this)
}
}
}
}
fun <T> storeValueInViewModelScope(key: Preferences.Key<T>, value: T) {
viewModelScope.launch {
dataStoreManager.storeValue(key, value)
}
}
}