【问题标题】:How to use AssistedInject to pass dynamic value as a parameter to ViewModel when using Dagger2 in Android在Android中使用Dagger2时如何使用AssistedInject将动态值作为参数传递给ViewModel
【发布时间】:2021-01-16 19:53:38
【问题描述】:

我是 Android 中 Dagger 2 的新手。我无法理解如何使用动态值注入 ViewModel。到目前为止,我已经使用带有预定义存储库依赖项的 dagger 多重绑定成功地注入了 ViewModel。这是我的代码。

应用组件

@Singleton
@Component(modules = [AppModule::class, SubComponentsModule::class, ViewModelFactoryModule::class])
interface ApplicationComponent {

    @Component.Factory
    interface Factory {
        fun create(@BindsInstance applicationContext: Context): ApplicationComponent
    }

    fun activityComponent(): ActivitySubComponent.Factory
    fun fragmentComponent(): FragmentSubComponent.Factory

}

片段模块

@Module
abstract class FragmentModule {

    @Binds
    @IntoMap
    @ViewModelKey(WeatherViewModel::class)
    abstract fun bindWeatherView(weatherViewModel: WeatherViewModel) : ViewModel

}

ViewModelFactoryModule

@Module
class ViewModelFactoryModule {

    @Provides
    @Singleton
    fun viewModelFactory(providerMap: Map<Class<out ViewModel>, Provider<ViewModel>>): ViewModelProvider.Factory {
        return ViewModelFactory(providerMap)
    }
}

应用类

class ThisApplication: Application(),InjectorProvider {

    override fun onCreate() {
        super.onCreate()

        Stetho.initializeWithDefaults(this)
    }

    override val component by lazy {
        DaggerApplicationComponent.factory().create(applicationContext)
    }

}

我正在使用InjectorProvider 接口来获取片段和活动,而不必每次都进行投射。

InjectorProvider

interface InjectorProvider {
  val component: ApplicationComponent
}

val Activity.injector get() = (application as InjectorProvider).component
val Fragment.injector get() = (requireActivity().application as InjectorProvider).component

这是我用来测试ViewModel注入的简单ViewModel。

WeatherViewModel

class WeatherViewModel @Inject constructor(val repository: WeatherRepository): ViewModel() {

    fun printMessage(){
        Log.d("WeatherViewModel","ViewModel binding is working")
        repository.printMessage()
    }

}

最后,我将这个视图模型注入到一个片段中,如下所示。

天气片段

class WeatherFragment : Fragment() {

    @Inject
    lateinit var viewModelFactory: ViewModelFactory

    override fun onAttach(context: Context) {
        injector.fragmentComponent().create().injectWeatherFragment(this)
        super.onAttach(context)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {

        val mainActivityViewModel =
            ViewModelProvider(this,viewModelFactory)[WeatherViewModel::class.java]
        mainActivityViewModel.printMessage()
    }

}

这部分工作正常。 printMessage() 内的日志消息被打印出来。我在匕首问题讨论中看到使用AssistedInject 是处理这种情况的最佳方法。我通过添加一个简单的 int 值作为参数更改了我的 ViewModle。

编辑的 WeatherViewModel

class WeatherViewModel @AssistedInject constructor(val repository: WeatherRepository,
                                                   @Assisted val id: Int): ViewModel() {

    @AssistedInject.Factory
    interface Factory{ fun create(id: Int) : WeatherViewModel }

    fun printMessage(){
        Log.d("WeatherViewModel","ViewModel binding is working")
        repository.printMessage()
    }
}

编辑的应用程序组件

@Singleton
@Component(modules = [AppModule::class, SubComponentsModule::class, ViewModelFactoryModule::class, AssistedInjectModule::class])
interface ApplicationComponent {

    @Component.Factory
    interface Factory {
        fun create(@BindsInstance applicationContext: Context): ApplicationComponent
    }

    fun activityComponent(): ActivitySubComponent.Factory
    fun fragmentComponent(): FragmentSubComponent.Factory

}

@AssistedModule
@Module(includes = [AssistedInject_AssistedInjectModule::class])
interface AssistedInjectModule

从现在开始,我不明白如何将ViewModel 注入到带有存储库和动态“id”值的片段中。如果我通过调用 create 方法 (val mainActivityViewModel = factory.create(5)) 将WeatherViewModel.Factory 注入到片段中,它将无法满足ViewModel 中的存储库依赖关系。如何将这两种解决方案结合起来,以实现具有动态值的预定义存储库依赖关系?或者有没有其他更好的方法来解决这个问题?

【问题讨论】:

    标签: android dagger-2 android-viewmodel


    【解决方案1】:

    不太清楚为什么您的设置无法通过使用工厂的create() 方法来满足repository 依赖项。 repository 依赖将由 Dagger's Acyclic Dependency Graph 提供。

    例如,下面我要对Dagger 说,我负责提供SavedStateHandle 和NavigationDispatcher,所以不要费心在你的acyclic dependency graph 中查找这些内容。

    class ProfileViewModel @AssistedInject constructor(
      @Assisted val handle: SavedStateHandle,
      @Assisted val navigationDispatcher: NavigationDispatcher,
      private val eventTracker: EventTracker,
      private val getUserUseCase: GetUserUseCase,
      private val logOutUseCase: LogOutUseCase
    ) : ViewModel(), ProfileHandler {
      @AssistedInject.Factory
      interface Factory {
        fun create(
          handle: SavedStateHandle,
          navigationDispatcher: NavigationDispatcher
        ): ProfileViewModel
      }
    

    在Fragment 方面,我在create 方法中提供的所有内容将是我用@Assisted 标记的dependencies 来履行我的承诺。

    class ProfileFragment : Fragment() {
      private val navigationDispatcher by getActivityViewModel {
        getBaseComponent().navigationDispatcher
      }
      private val eventTracker by lazy {
        getProfileComponent().eventTracker
      }
      private val viewModel by getViewModel { savedStateHandle ->
        getProfileComponent().profileViewModelFactory.create(savedStateHandle, navigationDispatcher)
      }
    

    getViewModel 只是一个extension 函数,如下所示:

    inline fun <reified T : ViewModel> Fragment.getViewModel(crossinline provider: (handle: SavedStateHandle) -> T) =
      viewModels<T> {
        object : AbstractSavedStateViewModelFactory(this, arguments) {
          override fun <T : ViewModel?> create(
            key: String,
            modelClass: Class<T>,
            handle: SavedStateHandle
          ) = provider(handle) as T
        }
      }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-02-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多