kotlin + Dagger2 :没有@Provides-annotated 方法就无法提供

Posted

技术标签:

【中文标题】kotlin + Dagger2 :没有@Provides-annotated 方法就无法提供【英文标题】:kotlin + Dagger2 : cannot be provided without an @Provides-annotated method 【发布时间】:2018-08-04 04:54:25 【问题描述】:

我不明白为什么会出现此错误:

Error:(12, 2) error: [dagger.android.AndroidInjector.inject(T)] java.util.Map<java.lang.Class<? extends android.arch.lifecycle.ViewModel>,? extends javax.inject.Provider<android.arch.lifecycle.ViewModel>> cannot be provided without an @Provides-annotated method.
public abstract interface ApplicationComponent 
                ^
      java.util.Map<java.lang.Class<? extends android.arch.lifecycle.ViewModel>,? extends javax.inject.Provider<android.arch.lifecycle.ViewModel>> is injected at
          com.chintansoni.android.architecturecomponentsblueprint.viewmodel.KotlinViewModelFactory.<init>(creators)
      com.chintansoni.android.architecturecomponentsblueprint.viewmodel.KotlinViewModelFactory is injected at
          com.chintansoni.android.architecturecomponentsblueprint.view.activity.SplashActivity.viewModelFactory
      com.chintansoni.android.architecturecomponentsblueprint.view.activity.SplashActivity is injected at
          dagger.android.AndroidInjector.inject(arg0)

KotlinApplication.kt

class KotlinApplication : Application(), HasActivityInjector 

    @Inject
    lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Activity>

    override fun onCreate() 
        super.onCreate()
        initializeLogger()
        initializeAppInjector()
    

    private fun initializeAppInjector() 
        AppInjector.init(this)
    

    private fun initializeLogger() 
        if (BuildConfig.DEBUG) 
            Timber.plant(Timber.DebugTree())
        
    

    override fun activityInjector(): DispatchingAndroidInjector<Activity>? 
        return dispatchingAndroidInjector
    

AppInjector.kt

object AppInjector 
    fun init(kotlinApplication: KotlinApplication) 
        DaggerApplicationComponent.builder()
                .application(kotlinApplication)
                .build()
                .inject(kotlinApplication)

        kotlinApplication.registerActivityLifecycleCallbacks(object : Application.ActivityLifecycleCallbacks 

            override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) 
                handleActivity(activity)
            

            override fun onActivityStarted(activity: Activity) 

            

            override fun onActivityResumed(activity: Activity) 

            

            override fun onActivityPaused(activity: Activity) 

            

            override fun onActivityStopped(activity: Activity) 

            

            override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) 

            

            override fun onActivityDestroyed(activity: Activity) 

            
        )
    

    private fun handleActivity(activity: Activity) 
        if (activity is HasSupportFragmentInjector) 
            AndroidInjection.inject(activity)
        
        (activity as? FragmentActivity)?.supportFragmentManager?.registerFragmentLifecycleCallbacks(
                object : FragmentManager.FragmentLifecycleCallbacks() 
                    override fun onFragmentCreated(fm: FragmentManager, f: Fragment,
                                                   savedInstanceState: Bundle) 
                        if (f is Injectable) 
                            AndroidSupportInjection.inject(f)
                        
                    
                , true)
    

ApplicationComponent.kt

@Singleton
@Component(modules = [(AndroidSupportInjectionModule::class), (AppModule::class), (SplashActivityModule::class)])
interface ApplicationComponent 

    @Component.Builder
    interface Builder 

        @BindsInstance
        fun application(application: Application): Builder

        fun build(): ApplicationComponent
    

    fun inject(kotlinApplication: KotlinApplication)

AppModule.kt

@Module(includes = [(ViewModelModule::class)])
class AppModule 
    @Singleton
    @Provides
    fun provideApiService(): ApiService 
        return Retrofit.Builder()
                .baseUrl("https://randomuser.me/")
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(LiveDataCallAdapterFactory())
                .build()
                .create(ApiService::class.java)
    

ViewModelModule.kt

@Module
abstract class ViewModelModule 

    @Binds
    @IntoMap
    @ViewModelKey(SplashViewModel::class)
    abstract fun bindSplashViewModel(splashViewModel: SplashViewModel): ViewModel

    @Binds
    abstract fun bindViewModelFactory(kotlinViewModelFactory: KotlinViewModelFactory): ViewModelProvider.Factory

ViewModelKey.kt

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

KotlinViewModelFactory.kt

@Singleton
class KotlinViewModelFactory @Inject constructor(private val creators: Map<Class<out ViewModel>, Provider<ViewModel>>) : ViewModelProvider.Factory 
    @Suppress("UNCHECKED_CAST")
    override fun <T : ViewModel?> create(modelClass: Class<T>): T 
        var creator: Provider<out ViewModel>? = creators[modelClass]
        if (creator == null) 
            for ((key, value) in creators) 
                if (modelClass.isAssignableFrom(key)) 
                    creator = value
                    break
                
            
        
        if (creator == null) 
            throw IllegalArgumentException("unknown model class " + modelClass)
        
        try 
            return creator.get() as T
         catch (e: Exception) 
            throw RuntimeException(e)
        
    

如果有人可以帮助我解决此问题,我已经浪费了大量时间来查找代码中的错误。 :(

【问题讨论】:

我认为你必须提供 splashViewModel: SplashViewModel & kotlinViewModelFactory: KotlinViewModelFactory 查看这里关于Kotlin Wildcards @DavidMedenjak 宾果游戏!!谢谢指点。 【参考方案1】:

我只是想在Provider&lt;ViewModel&gt; 之前添加@JvmSuppressWildcards

@Singleton
class KotlinViewModelFactory @Inject constructor(private val creators: Map<Class<out ViewModel>, @JvmSuppressWildcards Provider<ViewModel>>) : ViewModelProvider.Factory 
    ...

愿上帝帮助我们所有人使用 Kotlin + Dagger :)

我写了一篇文章来解决这个Dagger的迷宫,让开发者的生活更轻松:

https://medium.com/simform-engineering/stabbing-the-dagger-in-kotlin-merely-in-4-mins-977dba02fade

【讨论】:

有同样的问题,好吧,添加@JvmSuppressWildcards 现在获取ApiService 不能提供没有@Provides 方法。 :|男人! 就我而言,这是 Kotlin 1.3.30 的问题。尝试将版本更新到 1.3.31。【参考方案2】:

就我而言,我忘记在class AppModule 中包含@Module(includes = [ViewModelModule::class])

【讨论】:

以上是关于kotlin + Dagger2 :没有@Provides-annotated 方法就无法提供的主要内容,如果未能解决你的问题,请参考以下文章

Kotlin + Dagger2:不能在没有 @Inject 构造函数或 @Provides- 或 @Produces-annotated 方法的情况下提供

Dagger2 + Kotlin:lateinit 属性尚未初始化

Dagger2 Qualifier 不适用于 Kotlin?

dagger2 和 kotlin 的 Android 单元测试问题

Android kotlin 使用Dagger2报错 InvocationTargetException 不能生成DaggerXXComponent

kotlin 缺少提供程序的 Dagger 2 错误