Convert View Model to Use Hilt Dependency Injection

August 6, 2022


Here are the steps I did and my key learnings to convert my Android News app to use hilt dependency injection

This article was originally published at vtsen.hashnode.dev on July 16, 2022.

So I followed the steps in How to Implement Hilt in Android App? article to convert the Android News app to use Hilt Dependency Injection. The difference in previous article is it doesn’t cover the View Model dependency injection.



1. Add Dependencies and @HiltAndroidApp

The initial setup steps are exactly the same as the following:



2. Add @HiltViewModel and @Inject Constructor

In order for Hilt to create MainViewModel, you need to annotate the class with @HiltViewModel. @Inject constructor is also used to tell Hilt how the dependencies can be created.

@HiltViewModel
class MainViewModel
    @Inject constructor(
        private val repository: ArticlesRepository,
    ) : ViewModel() {
    ...
}
Enter fullscreen modeExit fullscreen mode

I also use @Inject Constructor on the SqlArticlesRepository.

class SqlArticlesRepository @Inject constructor(
    private val database: ArticlesDatabase,
    private val webService: WebService,
) : ArticlesRepository {
   ...
}
Enter fullscreen modeExit fullscreen mode



3. Add @Provides and @Binds

To create the dependencies, we use either @Provides and @Binds. @Provides is used to create ArticlesDatabase and WebService instances.

@Module
@InstallIn(SingletonComponent::class)
object DatabaseModule {

    @Provides
    @Singleton
    fun provideDatabase(@ApplicationContext appContext: Context): ArticlesDatabase {

        return Room.databaseBuilder(
            appContext,
            ArticlesDatabase::class.java,
            "articles.db",
        )
            .fallbackToDestructiveMigration()
            .build()
    }
}

@Module
@InstallIn(SingletonComponent::class)
object WebServiceModule {

    @Provides
    @Singleton
    fun provideWebService(): WebService {
        return WebService()
    }
}
Enter fullscreen modeExit fullscreen mode

@Binds is used to create the implementation of ArticlesRepository interface.

@Module
@InstallIn(SingletonComponent::class)
abstract class RepositoryModule {

    @Binds
    @Singleton
    abstract fun bindArticlesRepository(impl: SqlArticlesRepository): ArticlesRepository
}
Enter fullscreen modeExit fullscreen mode



4. Call hiltViewModel() Composable

Now it is done! All manual creations below in MainActivity can be removed.

private val repository by lazy {
    SqlArticlesRepository(
        ArticlesDatabase.getInstance(application),
        WebService(),
    )
}

private val homeViewModel by viewModels<MainViewModel> {
    MainViewModelFactory(repository)
}
Enter fullscreen modeExit fullscreen mode

The MainViewModel creation can be done by calling the hiltViewModel() composable function, which generates all the necessary dependencies.

For example, replace this MainScreen composable function

MainScreen(homeViewModel, useSystemUIController = true)
Enter fullscreen modeExit fullscreen mode

with

MainScreen(viewModel = hiltViewModel(), useSystemUIController = true)
Enter fullscreen modeExit fullscreen mode

The database creation below can also be removed since it has been provided by hilt @Provides above.

companion object {
    @Volatile
    private lateinit var instance: ArticlesDatabase

    fun getInstance(context: Context): ArticlesDatabase {
        synchronized(this) {
            if (!::instance.isInitialized) {
                instance = Room.databaseBuilder(
                    context.applicationContext,
                    ArticlesDatabase::class.java,
                    "articles.db")
                    .fallbackToDestructiveMigration()
                    .build()
            }

            return instance
        }
    }
}
Enter fullscreen modeExit fullscreen mode

This MainViewModelFactory can also be removed since this has been taken care by @HiltViewModel.

@Suppress("UNCHECKED_CAST")
class MainViewModelFactory(private val repository: ArticlesRepository)
    : ViewModelProvider.NewInstanceFactory() {

    override fun <T : ViewModel> create(modelClass: Class<T>): T {

        if (modelClass.isAssignableFrom(MainViewModel::class.java)) {
            return MainViewModel(repository) as T
        }

        throw IllegalArgumentException("Unknown ViewModel class")
    }
}
Enter fullscreen modeExit fullscreen mode



Conclusion

All my installed Hilt modules are in SingletonComponent scope. If I change my installed module to another component, for example ViewModelComponent or ActivityComponent, it fails to compile, and I don’t know how to fix it I guess that is my next step to play around with this Hilt.

Yes, I still don’t like Hilt. Call me grand pa, I still prefer manual dependency inject like this one:



Source Code



See Also



Source link

Comments 0

Leave a Reply

Your email address will not be published. Required fields are marked *