Я изо всех сил пытаюсь внедрить библиотеку настроек DataStore в Android Jetpack Compose, чтобы сохранить некоторые пользовательские настройки в моем приложении. Всякий раз, когда я пытаюсь получить доступ к SettingsViewModel из моего компонента Composable, приложение падает, и я получаю следующую ошибку:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.boldmethod, PID: 5415
java.lang.RuntimeException: Cannot create an instance of class com.boldmethod.Models.SettingsViewModel
...
Caused by: java.lang.InstantiationException: java.lang.Class<com.packageName.Models.SettingsViewModel> has no zero argument constructor
Я следую документам, чтобы создать DataStore и модель представления, поэтому, возможно, я неправильно использую их в Composable. вот соответствующий исходный код:
Грейдл
dependencies {
// Kotlin/Core
implementation "org.jetbrains.kotlin:kotlin-stdlib:1.4.20"
implementation 'androidx.core:core-ktx:1.3.2'
implementation 'androidx.appcompat:appcompat:1.2.0'
// Compose
implementation "androidx.compose.ui:ui:1.0.0-alpha08"
implementation "androidx.compose.material:material:1.0.0-alpha08"
implementation "androidx.compose.runtime:runtime:1.0.0-alpha08"
implementation "androidx.compose.runtime:runtime-livedata:1.0.0-alpha08"
implementation "androidx.compose.runtime:runtime-rxjava2:1.0.0-alpha08"
// Navigation
def nav_compose_version = "1.0.0-alpha03"
implementation "androidx.navigation:navigation-compose:$nav_compose_version"
// Architecture
implementation "androidx.datastore:datastore-preferences:1.0.0-alpha05"
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0'
//implementation "android.arch.lifecycle:runtime:2.1.0"
// UI/Material
implementation 'com.google.android.material:material:1.2.1'
implementation "androidx.ui:ui-tooling:1.0.0-alpha07"
// Testing
testImplementation 'junit:junit:4.13.1'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}
Репозиторий пользовательских настроек, который создает DataStore
class UserPreferencesRepository private constructor(context: Context) {
data class UserPreferences(
val resolution: String,
val theme: String,
)
private val dataStore: DataStore<Preferences> =
context.createDataStore(name = "userPreferences")
private object PreferencesKeys {
val RESOLUTION = preferencesKey<String>("resolution")
val THEME = preferencesKey<String>("theme")
}
// Read from the DataStore with a Flow object.
val userPreferencesFlow: Flow<UserPreferences> = dataStore.data
.catch { exception ->
if (exception is IOException) {
emit(emptyPreferences())
} else {
throw exception
}
}.map { preferences ->
// Get values or a default value.
val resolution = preferences[PreferencesKeys.RESOLUTION] ?: "HD"
val theme = preferences[PreferencesKeys.THEME] ?: "Auto"
UserPreferences(resolution, theme)
}
// Edit the DataStore.
suspend fun updateResolution(resolution: String) {
dataStore.edit { preferences ->
// when statement here to change more than just res.
preferences[PreferencesKeys.RESOLUTION] = resolution
}
}
}
Настройки ViewModel
class SettingsViewModel(
userPreferencesRepository: UserPreferencesRepository
) : ViewModel() {
private val _resolution = MutableLiveData("HD")
val resolution: LiveData<String> = _resolution
fun onResolutionChanged(newResolution: String) {
when (newResolution) {
"540p" -> _resolution.value = "720p"
"720p" -> _resolution.value = "HD"
"HD" -> _resolution.value = "540p"
}
}
}
Компонент настроек
@Composable
fun Settings(
settingsViewModel: SettingsViewModel = viewModel(),
) {
val resolution: String by settingsViewModel.resolution.observeAsState("")
ScrollableColumn(Modifier.fillMaxSize()) {
Column {
...
Card() {
Row() {
Text(
text = "Resolution",
)
Text(text = resolution,
modifier = Modifier.clickable(
onClick = { settingsViewModel.onResolutionChanged(resolution) },
indication = null))
}
}
...
}
}
}
Попытка заставить его работать с Compose была для меня борьбой, и я был бы рад любой помощи. Спасибо!
Поскольку ваша SettingsViewModel использует UserPreferencesRepository в качестве параметра конструктора, вы должны предоставить фабричный метод для создания экземпляра ViewModel:
class SettingsViewModelFactory(private val userRepository:UserRepository) : ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(SettingsViewModel::class.java)) {
return SettingsViewModel(userRepository) as T
}
throw IllegalArgumentException("Unknown ViewModel class")
}
}
val userRepository = UserRepository(context)
val viewModel: SettingsViewModel by viewModels { SettingsViewModelFactory(userRepository)}
Если вы не предоставите фабрику для viewModels(), будет использоваться defaultViewModelProviderFactory, который может создавать только экземпляры моделей представления, которые имеют конструктор с нулевым аргументом.
JNS, большое спасибо, amigo, это второй вопрос о Jetpack, на который вы мне ответили на этой неделе; ты звезда! Ваш ответ заставил меня двигаться в правильном направлении, чтобы найти решение, серьезно, спасибо.