简体   繁体   English

在Android中使用Dagger2时如何使用AssistedInject将动态值作为参数传递给ViewModel

[英]How to use AssistedInject to pass dynamic value as a parameter to ViewModel when using Dagger2 in Android

I am new to Dagger 2 in android.我是 android 中 Dagger 2 的新手。 I am having trouble understanding how to inject ViewModel with dynamic value.我无法理解如何为ViewModel注入动态值。 So Far I have successfully injected ViewModel using dagger multi binding with pre-defined repository dependency.到目前为止,我已经使用带有预定义存储库依赖项的 dagger 多重绑定成功地注入了 ViewModel。 Here's my code.这是我的代码。

ApplicationComponent应用组件

@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

}

FragmentModule片段模块

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

Application class应用 class

class ThisApplication: Application(),InjectorProvider {

    override fun onCreate() {
        super.onCreate()

        Stetho.initializeWithDefaults(this)
    }

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

}

I'm using InjectorProvider interface to get dagger to fragments and activity without having to cast every time.我正在使用InjectorProvider接口来获得碎片和活动的匕首,而不必每次都进行强制转换。

InjectorProvider注射器提供者

interface InjectorProvider {
  val component: ApplicationComponent
}

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

This is the simple ViewModel I used for testing ViewModel injection.这是我用于测试ViewModel注入的简单ViewModel

WeatherViewModel天气视图模型

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

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

}

Finally, I Injected this view model into a fragment like below.最后,我将此视图 model 注入到如下片段中。

WeatherFragment天气片段

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

}

This part is working fine.这部分工作正常。 The Log message inside printMessage() getting printed. printMessage()中的日志消息被打印出来。 I saw in the dagger issue discussion that using AssistedInject is the best approach to handle this kind of scenario.我在匕首问题讨论中看到,使用AssistedInject是处理这种情况的最佳方法。 I changed my ViewModle by adding a simple int value as a parameter.我通过添加一个简单的 int 值作为参数来更改我的ViewModle

Edited WeatherViewModel编辑了 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()
    }
}

Edited ApplicationComponent编辑的应用程序组件

@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

From this point onwards I don't understand how to inject ViewModel into fragment with repository plus dynamic "id" value.从这一点开始,我不明白如何将ViewModel注入到带有存储库和动态“id”值的片段中。 If I inject WeatherViewModel.Factory into the fragment by calling the create method ( val mainActivityViewModel = factory.create(5) ) it won't fulfill the repository dependency in ViewModel .如果我通过调用 create 方法( val mainActivityViewModel = factory.create(5) )将WeatherViewModel.Factory注入片段,它将无法满足ViewModel中的存储库依赖关系。 How to combine these two solutions to have pre-defined repository dependency with dynamic value?如何将这两种解决方案结合起来,以实现具有动态值的预定义存储库依赖关系? OR is there any other better way of approaching this?或者有没有其他更好的方法来解决这个问题?

Not quite sure why your setup wont fulfill repository dependency by using create() method of factory.不太清楚为什么您的设置无法通过使用工厂的create()方法来满足repository依赖关系。 The repository dependency will be provided by Dagger's Acyclic Dependency Graph . repository依赖将由Dagger's Acyclic Dependency Graph提供。

For example, below I'm saying to Dagger that I am responsible for providing SavedStateHandle and the NavigationDispatcher so don't even bother looking these up in your acyclic dependency graph .例如,下面我对Dagger说,我负责提供SavedStateHandleNavigationDispatcher ,所以甚至不用费心在你的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
  }

In Fragment side, all I have to provide in the create method will be the dependencies i marked with @Assisted to fulfil my side of promise.Fragment方面,我必须在create方法中提供的所有dependencies项都是我用@Assisted标记的,以实现我的 promise 方面。

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 is simply an extension function as follows: getViewModel只是一个extension function 如下:

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
    }
  }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM