【问题标题】:Dagger 2 providing ViewModel with component dependencyDagger 2 为 ViewModel 提供组件依赖
【发布时间】:2018-08-09 11:14:48
【问题描述】:

我正在使用带有多重绑定的 Dagger 2 来提供 ViewModels。这是我的代码:

interface AppProvider : MainToolsProvider

interface MainToolsProvider {
    fun provideAppContext(): Context
    fun provideSchedulers(): SchedulersProvider
    fun providerResourceManager(): ResourceManager
    fun provideViewModelFactory(): ViewModelProvider.Factory
    fun provideApi(): Api
}

@Singleton
@Component(modules = [ToolsModule::class, ViewModelModule::class])
interface MainToolsComponent : MainToolsProvider {

    @Component.Builder
    interface Builder {
        fun build(): MainToolsComponent

        @BindsInstance
        fun appContext(appContext: Context): Builder
    }

    class Initializer private constructor() {
        companion object {

            fun init(appContext: Context): MainToolsProvider =
                    DaggerMainToolsComponent.builder()
                            .appContext(appContext)
                            .build()
        }
    }
}

@Module
class ToolsModule {

    @Module
    companion object {
        @JvmStatic
        @Provides
        @Singleton
        fun provideSchedulers(): SchedulersProvider = AppSchedulers()

        @JvmStatic
        @Provides
        @Singleton
        fun provideResourceManager(appContext: Context) = ResourceManager(appContext)

        @JvmStatic
        @Provides
        @Singleton
        fun provideGson() = Gson()

        @JvmStatic
        @Provides
        @Singleton
        fun provideConverterFactory(gson: Gson): Converter.Factory = GsonConverterFactory.create(gson)

        @JvmStatic
        @Provides
        @Singleton
        fun provideAdapterFactory(): CallAdapter.Factory = RxJava2CallAdapterFactory.create()

        @JvmStatic
        @Provides
        @Singleton
        fun provideHttpClient() = OkHttpClient.Builder().build()

        @JvmStatic
        @Provides
        @Singleton
        fun provideRetrofit(httpClient: OkHttpClient, adapterFactory: CallAdapter.Factory, converterFactory: Converter.Factory) =
                Retrofit.Builder()
                        .baseUrl("https://api.clubinclub.com")
                        .addCallAdapterFactory(adapterFactory)
                        .addConverterFactory(converterFactory)
                        .client(httpClient)
                        .build()

        @JvmStatic
        @Provides
        @Singleton
        fun provideApi(retrofit: Retrofit) = retrofit.create(Api::class.java)

    }
}

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

@Component(
    dependencies = [MainToolsProvider::class]
)
@Singleton
interface AppComponent : AppProvider {

    fun inject(app: ClubInClubApp)

    class Initializer private constructor() {

        companion object {
            fun init(app: ClubInClubApp): AppComponent {
                val mainToolsProvider = MainToolsComponent.Initializer.init(app)
                return DaggerAppComponent.builder()
                        .mainToolsProvider(mainToolsProvider)
                        .build()
            }
        }
    }
}    

@ActivityScope
@Component(dependencies = [AppProvider::class], modules = [AuthModule::class])
interface AuthComponent {

    fun inject(activity: AuthActivity)
    fun inject(activity: InstagramAuthActivity)
    fun viewModelFactory(): ViewModelProvider.Factory

    class Initializer private constructor() {
        companion object {
            fun init(appProvider: AppProvider) =
                    DaggerAuthComponent.builder()
                            .appProvider(appProvider)
                            .build()
        }
    }
}

@Module
class AuthModule {

    @Provides
    @ActivityScope
    fun provideAuthRepository(api: Api): AuthRepository = AuthRepositoryImpl(api)

    @Provides
    @ActivityScope
    fun provideAuthInteractor(repository: AuthRepository, schedulers: SchedulersProvider): AuthInteractor = AuthInteractorImpl(repository, schedulers)
}

@FragmentScope
@Component(dependencies = [AuthComponent::class], modules = [SignUpSocialViewModelModule::class])
interface SignUpSocialComponent {

    fun inject(fragment: SignUpSocialFragment)

    class Initializer private constructor() {
        companion object {
            fun init(authComponent: AuthComponent) =
                    DaggerSignUpSocialComponent.builder()
                            .authComponent(authComponent)
                            .build()
        }
    }
}

@Module
abstract class SignUpSocialViewModelModule {

    @Binds
    @IntoMap
    @ViewModelKey(SignUpSocialViewModel::class)
    abstract fun signUpSocialViewModel(viewModel: SignUpSocialViewModel): ViewModel

}    

@Singleton
class ViewModelFactory @Inject constructor(
        val viewModels: Map<Class<out ViewModel>, @JvmSuppressWildcards Provider<ViewModel>>
) : ViewModelProvider.Factory {

    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        val provider = viewModels[modelClass]
        if (provider != null) {
            return provider.get() as T
        }
        throw IllegalArgumentException()
    }
}

@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER)
@Retention(AnnotationRetention.RUNTIME)
@MapKey
annotation class ViewModelKey(val value: KClass<out ViewModel>)

如您所见,我在ViewModelModule 中提供了ViewModelProvider.Factory,它使用组件依赖项包含在MainToolsComponentAppComponent 中。我也有AuthComponent 用于我的ActivityAppProvider 依赖和SignUpSocialComponentAuthComponent 依赖。还有我的片段:

class SignUpSocialFragment : BaseBindingFragment<FragmentSignUpSocialBinding>() {

    override val layoutId = R.layout.fragment_sign_up_social

    @Inject
    lateinit var viewModelFactory: ViewModelProvider.Factory
    private lateinit var viewModel: SignUpSocialViewModel

    private var callback: OnSocialButtonClickCallback? = null

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

        viewModel = ViewModelProviders.of(this, viewModelFactory)[SignUpSocialViewModel::class.java]
    }

    override fun initComponent() {
        (activity as? AuthActivity)?.run {
            SignUpSocialComponent.Initializer.init(component).inject(this@SignUpSocialFragment)
        }
    }   
}

方法initComponent在父片段的onCreate方法中被调用。问题是当我尝试编译和启动应用程序时,出现错误:

error: [Dagger/MissingBinding] [com.social.connect.clubinclub.di.base.MainToolsProvider.provideViewModelFactory()] java.util.Map<java.lang.Class<? extends android.arch.lifecycle.ViewModel>,javax.inject.Provider<android.arch.lifecycle.ViewModel>> cannot be provided without an @Provides-annotated method.
public abstract interface MainToolsComponent extends com.social.connect.clubinclub.di.base.MainToolsProvider {
            ^
  java.util.Map<java.lang.Class<? extends android.arch.lifecycle.ViewModel>,javax.inject.Provider<android.arch.lifecycle.ViewModel>> is injected at
      com.social.connect.clubinclub.presentation.base.ViewModelFactory.<init>(viewModels)
  com.social.connect.clubinclub.presentation.base.ViewModelFactory is injected at
     com.social.connect.clubinclub.di.app.ViewModelModule.bindViewModelFactory(factory)
  android.arch.lifecycle.ViewModelProvider.Factory is provided at
      com.social.connect.clubinclub.di.base.MainToolsProvider.provideViewModelFactory()

所以我不知道我做错了什么。请帮忙

【问题讨论】:

    标签: android dagger-2 android-viewmodel dagger


    【解决方案1】:

    仔细查看您的错误消息:

    在您的组件 (MainToolsProvider.provideViewModelFactory()) 中无法提供 Map&lt;.., ViewModel&gt;

    java.util.Map,javax.inject.Provider> 不能在没有@Provides-annotated 方法的情况下提供。


    检查你的代码我看不到你声明了@Multibinds,所以 Dagger 会抱怨,因为地图是空的。

    您要填充地图的唯一位置是SignUpSocialViewModelModule,然后将其添加到SignUpSocialComponent,但我们缺少MainToolsProvider 或者更确切地说MainToolsComponent 中的地图,如上所示。


    要解决编译错误,您可以声明 @Multibinds 以便至少有一个 Dagger 知道的空映射,但随后您将在运行时收到有关该类缺少提供程序的异常。您可能应该将@Binds @IntoMap ... ViewModel 位移动到MainToolsComponent

    【讨论】:

      【解决方案2】:

      MainToolsProvider 中删除fun provideViewModelFactory(): ViewModelProvider.Factory

      SignUpSocialFragment 中删除@Inject lateinit var viewModelFactory: ViewModelProvider.FactoryviewModel = ViewModelProviders.of(this, viewModelFactory)[SignUpSocialViewModel::class.java],取而代之的是@Inject lateinit var lateinit var viewModel: SignUpSocialViewModel

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-08-07
        • 1970-01-01
        • 2017-02-26
        • 1970-01-01
        • 2015-06-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多