StefanJo
StefanJo

Reputation: 285

Application dependency to ViewModel with HILT

I was wondering how can I pass application dependency to ViewModel using Hilt? I was trying with AndroidViewModel, but I couldn't make it. Can someone help me? Some short sample could will mean a lot to me.

This is my ViewModel:

class MainViewModel @ViewModelInject constructor(
    private val application: Application,
    private val repository: Repository,
    @Assisted private val savedStateHandle: SavedStateHandle
) : ViewModel() {

This is my hilt module

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

    @Singleton
    @Provides
    fun provideDatabase(
        @ApplicationContext context: Context
    ) = Room.databaseBuilder(
        context,
        MyDatabase::class.java,
        "my_database"
    ).build()

    @Singleton
    @Provides
    fun provideDao(database: MyDatabase) = database.myDao()

    @Singleton
    @Provides
    fun provideRepository(myDao: MyDao) = Repository(myDao)

    @Singleton
    @Provides
    fun provideApplicationContext() = MyApplication()

}

Everything else is fine, and I got the error message:

Caused by: java.lang.RuntimeException: Cannot create an instance of class com.example.example.viewmodel.MainViewModel Caused by: java.lang.InstantiationException: java.lang.Class<com.example.example.viewmodel.MainViewModel> has no zero argument constructor

Upvotes: 5

Views: 5465

Answers (2)

Patriotic
Patriotic

Reputation: 2300

Use @ApplicationContext Context context as a parameter in the constructor.

Upvotes: 0

You can see full source https://github.com/Kotlin-Android-Open-Source/MVI-Coroutines-Flow/tree/dagger_hilt

  • Repository:
@Singleton
class UserRepositoryImpl @Inject constructor(
    private val userApiService: UserApiService,
    private val dispatchers: CoroutineDispatchers,
    ...
) : UserRepository { ... }
  • Usecases:

class AddUserUseCase @Inject constructor(private val userRepository: UserRepository) {
  suspend operator fun invoke(user: User) = userRepository.add(user)
}

class RemoveUserUseCase @Inject constructor(private val userRepository: UserRepository) {
  suspend operator fun invoke(user: User) = userRepository.remove(user)
}

class RefreshGetUsersUseCase @Inject constructor(private val userRepository: UserRepository) {
  suspend operator fun invoke() = userRepository.refresh()
}

...
  • ViewModel:
class MainVM @ViewModelInject constructor(
    private val getUsersUseCase: GetUsersUseCase,
    private val refreshGetUsers: RefreshGetUsersUseCase,
    private val removeUser: RemoveUserUseCase,
) : ViewModel() { ... }
  • Activity:

@AndroidEntryPoint
class MainActivity : AppCompatActivity(), View {
  private val mainVM by viewModels<MainVM>()
  
  ...
}

Edited:

To inject application context:

First, remove this definition, because Hilt already provides application context:

    @Singleton
    @Provides
    fun provideApplicationContext() = MyApplication()

Second, Use @ApplicationContext annotation on your context parameter.

class MainViewModel @ViewModelInject constructor(
    @ApplicationContext private val context: Context,
    private val repository: Repository,
    @Assisted private val savedStateHandle: SavedStateHandle
) : ViewModel() {

Upvotes: 2

Related Questions