简体   繁体   English

Dagger2,在依赖组件中为 ViewModelProvider.Factory 添加绑定

[英]Dagger2, adding a binding for ViewModelProvider.Factory in a dependant component

The Problem问题

When attempting to add a ViewModel bind into the multibinding for an inherited ViewModelFactory (created with no scope) within a lower scope ( @FragmentScope ), I keep running into this error:当尝试将ViewModel绑定添加到较低范围 ( @FragmentScope ) 内继承的ViewModelFactory (创建时没有范围) 的@FragmentScope ,我不断@FragmentScope此错误:

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

What I've read or tried我读过或尝试过的

(note: the below is not by any means an exhaustive list, but are two good examples of resources and the kinds of advice I've perused) (注意:以下绝不是一个详尽的清单,而是我所阅读的资源和建议种类的两个很好的例子)

I'm relatively new to working with Dagger so I had to do a lot of Googling to try and understand what has been going on, but I've reached a point where, to my understanding, something should be working(?)...我对使用 Dagger 比较陌生,所以我不得不做很多谷歌搜索来尝试了解发生了什么,但我已经达到了一个点,据我所知,有些东西应该起作用(?).. .

From sources similar to [1], I removed the @Singleton scope on ViewModelFactory , but I still get the aforementioned crash saying there is no model class found in the mapping.从类似于 [1] 的来源中,我删除了ViewModelFactory上的@Singleton范围,但我仍然遇到上述崩溃,说在映射中找不到模型类。

From sources similar to [2] I tried to reinforce my understanding of how dependencies worked and how items are exposed to dependant components.从类似于 [2] 的来源,我试图加强我对依赖项如何工作以及项目如何暴露给依赖组件的理解。 I know and understand how ViewModelProvider.Factory is available to my MyFragmentComponent and it's related Modules.我知道并理解ViewModelProvider.Factory如何可用于我的MyFragmentComponent及其相关模块。

However I do not understand why the @Binds @IntoMap isn't working for the MyFragmentVM .但是我不明白为什么@Binds @IntoMap不适用于MyFragmentVM

The Code编码

Let me first go through the setup of the stuff that already exists in the application -- almost none of it was scoped for specific cases让我首先介绍一下应用程序中已经存在的东西的设置——几乎没有一个是针对特定情况的

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

And the following is how I am trying to add and utilize my @FragmentScope:以下是我尝试添加和利用我的 @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)
    }
}

What's interesting here to note is that MyFragmentModule itself does NOT end up providing any unique injections for MyFragment (those all come from AppComponent as it is right now).这里需要注意的是, MyFragmentModule本身并没有最终为MyFragment提供任何独特的注入(这些都来自 AppComponent,就像现在一样)。 It DOES however, provide unique injections for the ViewModel that MyFragment uses.但是,它确实为MyFragment使用的ViewModel提供了独特的注入。

The root of this problem is the difference between subcomponents and component dependencies .这个问题的根源在于子组件组件依赖之间的差异。

Subcomponents子组件

When working with subcomponents, the parent component knows everything about its subcomponents.当使用子组件时,父组件知道关于它的子组件的一切。 As such, when a subcomponent requests a multibinding, the parent component can combine its contributions with those of the subcomponent.因此,当子组件请求多重绑定时,父组件可以将其贡献与子组件的贡献结合起来。 This even works transitively: if the subcomponent requests an unscoped ViewModelProvider.Factory , the injected map will include bindings from the subcomponent.这甚至可以传递:如果子组件请求无作用域的ViewModelProvider.Factory ,则注入的映射将包含来自子组件的绑定。 (The same is true of a @Reusable binding, but not a @Singleton .) (同样适用于@Reusable绑定,但不适用于@Singleton 。)

If you change your components with dependencies into subcomponents, everything will just work.如果您将具有依赖项的组件更改为子组件,一切都会正常进行。 However, this might not fit your desired architecture.但是,这可能不适合您想要的架构。 In particular, this is impossible if MyFragmentComponent is in an Instant App module.特别是,如果MyFragmentComponent位于 Instant App 模块中,则这是不可能的。

Component dependencies组件依赖

When working with component dependencies, the main component merely exposes objects through provision methods, and it does not know about any components that might depend on it .在处理组件依赖时,主组件只是通过提供方法暴露对象,它不知道任何可能依赖它的组件 This time, when asked for a ViewModelProvider.Factory , the main component does not have access to any @ViewModelKey bindings except its own, and so the Factory it returns will not include the MyFragmentVM binding.这一次,当请求一个ViewModelProvider.Factory ,主组件除了它自己的之外不能访问任何@ViewModelKey绑定,因此它返回的Factory将不包括MyFragmentVM绑定。

If MyFragmentComponent does not require any ViewModel bindings from AppComponent , you can extract bindsViewModelFactory into its own module and include it in both components.如果MyFragmentComponent不需要来自AppComponent任何ViewModel绑定,您可以将bindsViewModelFactory提取到它自己的模块中并将其包含在两个组件中。 That way, both components can create their own Factory independently.这样,两个组件都可以独立创建自己的Factory

If you do need some ViewModel bindings from AppComponent , hopefully you can add those binding modules to MyFragmentComponent as well.如果您确实需要AppComponent一些ViewModel绑定,希望您也可以将这些绑定模块添加到MyFragmentComponent中。 If not, you would have to expose the map in AppComponent , and then somehow combine those entries with your new bindings.如果没有,您将不得不在AppComponent公开地图,然后以某种方式将这些条目与您的新绑定结合起来。 Dagger does not provide a good way to do this. Dagger 没有提供一个很好的方法来做到这一点。

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

相关问题 匕首2 viewmodels和ViewModelProvider.Factory - dagger 2 viewmodels and ViewModelProvider.Factory Dagger 2 ViewModelProvider.Factory绑定多次 - Dagger 2 ViewModelProvider.Factory bound multiple times 为什么在ViewModelProvider.Factory实现中添加@Singleton批注会导致编译错误[Dagger / MissingBinding]? - Why adding @Singleton annotation to ViewModelProvider.Factory implementation causes a compile error [Dagger/MissingBinding]? 使用Android上的Dagger和Java,ViewModelProvider.Factory在片段上保持为空 - ViewModelProvider.Factory remains null on fragment using Dagger and Java on Android 使用匕首将 ViewModelProvider.Factory 注入不同的 Activity - Inject ViewModelProvider.Factory to different Activity using dagger 将ViewModelProvider.Factory的提供程序注入espresso测试 - Inject provider of ViewModelProvider.Factory into esspresso test 如何将 ViewModelProvider.Factory 注入片段 - How to inject ViewModelProvider.Factory into a fragment ViewModelProvider.Factory 总是返回一个视图模型 - ViewModelProvider.Factory always return one viewmodel 获取ViewModel ViewModelProvider.Factory和应用程序上下文 - Get ViewModel ViewModelProvider.Factory and Application Context ViewModelProvider.Factory 和 ViewModelProvider.NewInstanceFactory 有什么区别? - What are the differences between ViewModelProvider.Factory and ViewModelProvider.NewInstanceFactory?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM