Jetpack Compose DataStore 使用 ViewModel 时出错,"Viewmodel has no zero argument constructor"
Jetpack Compose DataStore error using ViewModel, "Viewmodel has no zero argument constructor"
我正在努力在 Android Jetpack Compose 中实现 DataStore 首选项库以在我的应用程序中保留一些用户设置。每当我尝试从我的可组合组件访问 SettingsViewModel 时,应用程序崩溃并且我收到以下错误:
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 和视图模型,所以我可能没有在可组合项中正确使用它们。这是相关的源代码:
Gradle
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
}
}
}
设置视图模型
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,它只能实例化具有零参数构造函数的 viewModels
我正在努力在 Android Jetpack Compose 中实现 DataStore 首选项库以在我的应用程序中保留一些用户设置。每当我尝试从我的可组合组件访问 SettingsViewModel 时,应用程序崩溃并且我收到以下错误:
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 和视图模型,所以我可能没有在可组合项中正确使用它们。这是相关的源代码:
Gradle
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
}
}
}
设置视图模型
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,它只能实例化具有零参数构造函数的 viewModels