17

我试图提供一个通用DataStore<Preferences>的,以便可以在多个地方使用相同的首选项文件,但我得到了有用的错误消息:

找不到符号: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 不喜欢的事情,但即使问题无法解决,我也希望能解释正在发生的事情。

4

3 回答 3

26

这对我有用:

    @Provides
    @Singleton
    fun dataStore(@ApplicationContext appContext: Context): DataStore<Preferences> =
        appContext.createDataStore("settings")

这个想法@Singleton落后于提供者方法。


2021 年 2 月 9 日更新:
最好创建一个经理并提供:

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
    }

}
于 2021-02-08T12:34:57.330 回答
11

正如 Dr.jacky 提到的,现在推荐使用创建管理器的方式,但您仍然可以使用PreferenceDataStoreFactory和创建 Preferences DataStore 单例:

@Provides
@Singleton
fun providePreferencesDataStore(@ApplicationContext appContext: Context): DataStore<Preferences> =
    PreferenceDataStoreFactory.create(
        produceFile = {
            appContext.preferencesDataStoreFile(PREFERENCES_STORE_NAME)
        }
    )
于 2021-03-12T15:50:44.060 回答
4

DataStore<Preferences>与 Hilt 一起使用如下。

持久性模块.kt

@Module
@InstallIn(SingletonComponent::class)
object PersistenceModule {

    @Provides
    @Singleton
    fun provideDataStoreManager(@ApplicationContext context: Context): DataStoreManager {
        return DataStoreManager(context)
    }
}

数据存储管理器.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)
        }
    }
}

视图模型.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)
        }
    }
}
于 2021-06-11T08:48:59.887 回答