Зависимость приложения от ViewModel с помощью HILT

#android #kotlin #dagger-hilt

#Android #kotlin #кинжал-рукоять

Вопрос:

Мне было интересно, как я могу передать зависимость приложения в ViewModel с помощью Hilt? Я пытался использовать AndroidViewModel, но у меня не получилось. Кто-нибудь может мне помочь? Какой-нибудь короткий пример мог бы многое значить для меня.

Это моя ViewModel:

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

Это мой модуль hilt

 @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()

}
  

Все остальное в порядке, и я получил сообщение об ошибке:

Вызвано: java.lang.RuntimeException: Не удается создать экземпляр класса com.example.пример.viewmodel.MainViewModel вызвано: java.lang.Исключение экземпляра: java.lang.Класс<com.example.пример.viewmodel.MainViewModel> не имеет конструктора с нулевым аргументом

Комментарии:

1. Стефанджо, есть ли какое-либо решение, которого вы достигли, передавая экземпляр приложения в AndroidViewModel с использованием hilt?

Ответ №1:

Вы можете увидеть полный исходный код https://github.com/Kotlin-Android-Open-Source/MVI-Coroutines-Flow/tree/dagger_hilt

  • Репозиторий:
 @Singleton
class UserRepositoryImpl @Inject constructor(
    private val userApiService: UserApiService,
    private val dispatchers: CoroutineDispatchers,
    ...
) : UserRepository { ... }
  
  • Варианты использования:
 
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() { ... }
  
  • Активность:
 
@AndroidEntryPoint
class MainActivity : AppCompatActivity(), View {
  private val mainVM by viewModels<MainVM>()
  
  ...
}
  

Отредактировано:

Для введения контекста приложения:

Во-первых, удалите это определение, поскольку Hilt уже предоставляет контекст приложения:

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

Во-вторых, используйте аннотацию @ApplicationContext в вашем параметре контекста.

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

  

Комментарии:

1. Но у вас нет приложения, переданного в параметрах..

2. Обновленный ответ, надеюсь помочь вам

3. При этом отображается предупреждение This field leaks a context object

Ответ №2:

Используйте @ApplicationContext Context context в качестве параметра в конструкторе.