androidkotlinandroid-jetpack-composeandroid-room

How to get context for Room database and initialize it correctly in Android Compose?


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?


Solution

  • 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.