Ошибка Jetpack Compose DataStore с использованием ViewModel, «Viewmodel не имеет конструктора с нулевым аргументом»

Я изо всех сил пытаюсь внедрить библиотеку настроек 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 была для меня борьбой, и я был бы рад любой помощи. Спасибо!

1
0
2 002
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Поскольку ваша 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, на который вы мне ответили на этой неделе; ты звезда! Ваш ответ заставил меня двигаться в правильном направлении, чтобы найти решение, серьезно, спасибо.

Nicolas Mage 12.12.2020 00:28

Другие вопросы по теме