I use Room database in Android app, to create it instance of context
class is needed. I want to get access to database in ViewModel, but context can be obtained only in composables or activities, so i create ViewModel in MainActivity, and pass Application
(Application
inherits Context
) class to it to initialize DB. Then I pass ViewModel to top-level composable.
It works fine, but Android Guidelines say that this should be avoided:
Strongly recommended:
ViewModels shouldn't hold a reference to any Lifecycle-related type. Don't pass Activity, Fragment, Context or Resources as a dependency. If something needs a Context in the ViewModel, you should strongly evaluate if that is in the right layer.
and
Recommended:
Use the ViewModel class, not AndroidViewModel. The Application class shouldn't be used in the ViewModel. Instead, move the dependency to the UI or the data layer.
Where should I move database dependency then?
Hey you need to use dependency injection to get Dao of room database in viewmodel.
1.Hilt with Room in an Android application, including a ViewModel and DAO access
@Module
@InstallIn(SingletonComponent::class)
object AppModule {
@Provides
@Singleton
fun provideDatabase(@ApplicationContext context: Context): AppDatabase {
return Room.databaseBuilder(
context,
AppDatabase::class.java,
"app_database"
).build()
}
@Provides
@Singleton
fun provideUserDao(database: AppDatabase): UserDao {
return database.userDao()
}
}
Then in your viewmodel,
@HiltViewModel
class UserViewModel @Inject constructor(private val userDao: UserDao) : ViewModel() {
val users: LiveData<List<User>> = liveData {
emit(userDao.getAllUsers())
}
fun addUser(user: User) {
viewModelScope.launch {
userDao.insert(user)
}
}
}
Also add these entry points,
@HiltAndroidApp
class MyApplication : Application()
@AndroidEntryPoint
class MainActivity : AppCompatActivity()
2.Inject Application Context in ViewModel
@Module
@InstallIn(SingletonComponent::class)
object AppModule {
@Provides
@Singleton
fun provideApplicationContext(@ApplicationContext context: Context): Context {
return context
}
// Other provides methods can go here
}
@HiltViewModel
class MyViewModel @Inject constructor(
@ApplicationContext private val context: Context
) : ViewModel() {
// You can use the context here
}
Also, add entry point on activity and application.