Dagger2,在从属组件中添加ViewModelProvider.Factory的绑定

问题描述 投票:0回答:1

问题

[当尝试在较低范围(ViewModel)中为继承的ViewModelFactory(无范围创建)添加@FragmentScope绑定到多重绑定中时,我一直遇到此错误:

java.lang.IllegalArgumentException:未知模型类com.example.app.MyFragmentVM

我阅读或尝试过的内容

((注:下面绝不是详尽的清单,而是两个很好的示例资源和我所研究的建议)]

我刚开始与Dagger合作,所以我不得不做很多Google搜寻来尝试了解正在发生的事情,但是据我所知,我已经达到了应该起作用的地方(?) ...

从类似于[1]的源中,我删除了@Singleton上的ViewModelFactory范围,但仍然遇到上述崩溃,即在映射中找不到模型类。

[类似于[2]的资料,我试图加强对依存关系如何工作以及项目如何暴露于依存组件的理解。我知道并了解ViewModelProvider.Factory及其相关模块如何使用MyFragmentComponent

但是我不明白为什么@Binds @IntoMapMyFragmentVM不起作用。

代码

首先让我完成应用程序中已经存在的内容的设置-几乎所有内容都不适用于特定情况

// AppComponent
@Component(modules=[AppModule::class, ViewModelModule::class])
interface AppComponent {
    fun viewModelFactory(): ViewModelProvider.Factory

    fun inject(activity: MainActivity)
    // ... and other injections
}

// AppModule
@Module
class AppModule {
    @Provides
    @Singleton
    fun providesSomething(): Something

    // a bunch of other providers for the various injection sites, all @Singleton scoped
}

// ViewModelModule
@Module
abstract class ViewModelModule {
    @Binds
    abstract fun bindsViewModelFactory(factory: ViewModelFactory): ViewModelProvider.Factory

    @Binds
    @IntoMap
    @ViewModelKey(MainActivityVM::class)
    abstract fun bindsMainActivityVM(vm: MainActivityVM): ViewModel
}

// VMFactory
class ViewModelFactory @Injects constructor(
    private val creators: Map<Class<out ViewModel>, @JvmSuppressWildcards Provider<ViewModel>>
): ViewModelProvider.Factory {
    override fun <T : ViewModel?> create(modelClass: Class<T>): T {
        val creator = creators[modelClass] ?: creators.entries.firstOrNull {
            modelClass.isAssignableFrom(it.key)
        }?.value ?: throw IllegalArgumentException("unknown model class $modelClass")

        try {
            @Suppress("UNCHECKED_CAST")
            return creator.get() as T
        } catch (e: Exception) {
            throw RuntimeException(e)
        }
    }
}

以下是我尝试添加和利用我的@FragmentScope的方法:

// MyFragmentComponent
@FragmentScope
@Component(
    dependencies = [AppComponent::class],
    modules = [MyFragmentModule::class, MyFragmentVMModule::class]
)
interface MyFragmentComponent {
    fun inject(fragment: MyFragment)
}

// MyFragmentModule
@Module
class MyFragmentModule {
    @Provides
    @FragmentScope
    fun providesVMDependency(): VMDependency {
        // ...
    }
}

// MyFragmentVMModule
@Module
abstract class MyFragmentVMModule {
    @Binds
    @IntoMap
    @ViewModelKey(MyFragmentVM::class)
    abstract fun bindsMyFragmentVM(vm: MyFragmentVM): ViewModel
}

// MyFragment
class MyFragment : Fragment() {
    @set:Inject
    internal lateinit var vmFactory: ViewModelProvider.Factory

    private lateinit var viewModel: MyFragmentVM

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        DaggerMyFragmentComponent.builder()
            .appComponent(MyApplication.instance.component)
            .build()
            .inject(this)

        viewModel = ViewModelProvider(this, vmFactory).get(MyFragmentVM::class.java)
    }
}

[这里需要注意的有趣的是,MyFragmentModule本身并没有最终为MyFragment提供任何唯一的注入(所有注入均来自AppComponent,因为它现在是这样)。但是,它确实为ViewModel使用的MyFragment提供了唯一的注入。

android dependency-injection dagger-2
1个回答
1
投票
此问题的根源是

subcomponents和component依赖项>>之间的区别。子组件

使用子组件时,父组件了解有关其子组件的所有信息。这样,当子组件请求多重绑定时,父组件可以将其贡献与子组件的贡献结合在一起。这甚至可以过渡性地起作用:如果子组件请求不受作用域的ViewModelProvider.Factory,则注入的映射将包括来自子组件的绑定。 (@Reusable绑定也是如此,但@Singleton却不是。)

如果将具有依赖关系的组件更改为子组件,那么一切都会正常进行。但是,这可能不适合您所需的体系结构。特别是,如果MyFragmentComponent在Instant App模块中,则这是不可能的。

组件依赖项

使用组件依赖项时,主要组件仅通过供应方法公开对象,并且

它不知道可能依赖于它的任何组件。这次,当要求提供ViewModelProvider.Factory时,主要组件除具有自身的访问权限之外,无权访问任何@ViewModelKey绑定,因此它返回的Factory将不包括MyFragmentVM绑定。如果MyFragmentComponent不需要ViewModel的任何AppComponent绑定,则可以将bindsViewModelFactory提取到其自己的模块中并将其包含在两个组件中。这样,两个组件都可以独立创建自己的Factory

如果确实需要ViewModel中的某些AppComponent绑定,希望您也可以将这些绑定模块添加到MyFragmentComponent中。如果不是,则必须在AppComponent中公开地图,然后以某种方式将这些条目与新绑定结合在一起。 Dagger没有提供执行此操作的好方法。

© www.soinside.com 2019 - 2024. All rights reserved.