Reputation: 5141
I am new to Android development. Currently, I am using Jetpack Compose to build Android apps. I am also learning with MVVM architecture.
One thing I don't understand with this architecture is why we need to use ViewModelProvider.Factory
to pass view model to a screen.
For example,
Instead of this,
@Composable
fun HomeScreen() {
val factory = object : ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
val repository = InMemoryPlantService()
@Suppress("UNCHECKED_CAST")
return HomeViewModel(
plantRepository = repository
) as T
}
}
val homeViewModel: HomeViewModel = viewModel(null, factory)
val currentState: State<HomeViewState> = homeViewModel.viewState.collectAsState()
HomeScreenScaffold(currentState.value)
}
Can't we do this,
@Composable
fun HomeScreen() {
val repository = InMemoryPlantService()
val homeViewModel: HomeViewModel = HomeViewModel(
plantRepository = repository
)
val currentState: State<HomeViewState> = homeViewModel.viewState.collectAsState()
HomeScreenScaffold(currentState.value)
}
Please help.
Full source code can be found here: https://github.com/adammc331/bloom
HomeScreen
can be found here: https://github.com/AdamMc331/Bloom/blob/development/app/src/main/java/com/adammcneilly/bloom/HomeScreen.kt
Upvotes: 4
Views: 8228
Reputation: 6863
As per a codelab in Room,
By using viewModels and ViewModelProvider.Factory,the framework will take care of the lifecycle of the ViewModel. It will survive configuration changes and even if the Activity is recreated, you'll always get the right instance of the WordViewModel class.
Upvotes: 1
Reputation: 11
You do not have to use ViewModelProvider.Factory to instantiate your ViewModel.
Lets assume you have an Entity:
@Entity(tableName = "user")
data class User(
@PrimaryKey(autoGenerate = true) @ColumnInfo(name = "user_id") val userId: Long)
And a DAO for that entity:
@Dao
interface UserDao {//some methods}
Without using a repository you can instantiate your ViewModel with the help of android.app.Application
like so:
class UserViewModel(
application: Application
) : AndroidViewModel(application) {
val dao = AppDatabase.getDatabase(application, viewModelScope).userDao()
}
And then later in a Fragment create your ViewModel which you can later pass into your composable:
private val userViewModel: userViewModel by viewModels()
Upvotes: 0
Reputation: 16534
When you call:
val homeViewModel: HomeViewModel = viewModel(null, factory)
The function viewModel(...)
will create a new HomeViewModel
if it's the first time you request the ViewModel, or it will return the previous instance of HomeViewModel
if it already exists. That's one of the advantages of using ViewModels, because on configuration change (or on recomposition) your ViewModel should be reused, not created again. And the way it works is by using a ViewModelProvider.Factory
to create the ViewModel when it's necessary. Your ViewModel has a parameter on its constructor, there's no way the default Android classes would know how to create your ViewModel and pass that parameter (i.e. the repository) without you providing a custom ViewModelProvider.Factory
. If your ViewModel doesn't have any parameters, the default ViewModelProvider.Factory
uses reflection to create your class by using the no-argument constructor.
If you do this:
val homeViewModel: HomeViewModel = HomeViewModel(
plantRepository = repository
)
Your ViewModel will be created many times and won't be reused across configuration changes or recompositions because you're always creating it there - instead of asking for it to be created or reusing it if it already exists, which is what the viewModel(...)
function does.
Upvotes: 12