代码之家  ›  专栏  ›  技术社区  ›  kibar

Android Kotlin dagger 2 ViewModel注入错误

  •  0
  • kibar  · 技术社区  · 7 年前

    我正在尝试使用新的 ViewModel 来自arch android library。

    Gradle控制台:

    ....AppComponent.java:6: error: [dagger.android.AndroidInjector.inject(T)] kibar.app.ui.fragment.HomeFragmentViewModel cannot be provided without an @Inject constructor or from an @Provides-annotated method.
    
    e: public abstract interface AppComponent {
    e:                 ^
    e:       kibar.app.ui.fragment.vpresenter.home.HomeFragmentViewModel is injected at
    e:           kibar.core.di.ViewModelModule.bindHomeViewModel(model)
    e:       java.util.Map<java.lang.Class<? extends android.arch.lifecycle.ViewModel>,javax.inject.Provider<android.arch.lifecycle.ViewModel>> is injected at
    e:           kibar.core.di.viewmodel.ViewModelFactory.<init>(creators)
    e:       kibar.core.di.viewmodel.ViewModelFactory is injected at
    e:           kibar.app.ui.fragment.HomeFragment.viewModelFactory
    e:       kibar.app.ui.fragment.HomeFragment is injected at
    e:           dagger.android.AndroidInjector.inject(arg0)
    e: java.lang.IllegalStateException: failed to analyze: org.jetbrains.kotlin.kapt3.diagnostic.KaptError: Error while annotation processing
        at org.jetbrains.kotlin.analyzer.AnalysisResult.throwIfError(AnalysisResult.kt:57)
        at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.compileModules(KotlinToJVMBytecodeCompiler.kt:138)
        at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:154)
        at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:58)
        at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.java:103)
        at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.java:51)
        at org.jetbrains.kotlin.cli.common.CLITool.exec(CLITool.kt:92)
        ...
        ...
        ...
    

    代码:

    class App : Application(), HasActivityInjector {
    
        @field:Inject lateinit var component: AppComponent
        @field:Inject lateinit var injector: DispatchingAndroidInjector<Activity>
    
        override fun activityInjector() = injector
        override fun onCreate() {
            super.onCreate();
    
            DaggerAppComponent.builder()
                    .appModule(AppModule(this))
                    .build()
                    .apply { inject(this@App); component = this }
                    .inject(this)
        }
    
    }
    
    @AppScope
    @Component(
            modules = arrayOf(
                    AndroidSupportInjectionModule::class,
                    AppModule::class,
                    ActivityModule::class,
                    ViewModelModule::class,
                    DatabaseModule::class)
    )
    interface AppComponent {
    
        interface Builder {
            @BindsInstance
            fun application(app: App): Builder
    
            fun build(): AppComponent
        }
    
        fun inject(application: App)
    
    }
    
    @Module
    class AppModule(private val application: Application) {
    
        @Provides
        @AppScope
        fun provideApplication(): Application = application
    
        @Provides
        @AppScope
        @ApplicationContext
        fun provideContext(): Context = application.applicationContext!!
    
    }
    
    @Module
    abstract class ActivityModule {
    
        @ContributesAndroidInjector(modules = arrayOf(FragmentModule::class))
        abstract fun bindHomeActivity(): HomeActivity
    
    }
    
    @Module(includes = arrayOf(AppModule::class))
    class DatabaseModule {
    
        @AppScope
        @Provides
        fun provideDatabase(@ApplicationContext context: Context): RoomDatabase =
                Room.databaseBuilder(context, RoomDatabase::class.java, RoomDatabase.CONS.NAME)
                    .allowMainThreadQueries()
                    .build()
    
    }
    
    @Module
    abstract class FragmentModule {
    
        @ContributesAndroidInjector
        abstract fun provideHomeFragment(): HomeFragment
    
    }
    
    @Module
    abstract class ViewModelModule {
    
        @Binds
        @IntoMap
        @ViewModelKey(HomeFragmentViewModel::class)
        abstract fun bindHomeViewModel(model: HomeFragmentViewModel): ViewModel
    
        @Binds
        abstract fun bindAppViewModelFactory(factory: ViewModelFactory): ViewModelProvider.Factory
    
    }
    
    @AppScope
    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 {
            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)
            }
        }
    
    }
    
    @MustBeDocumented
    @Target(AnnotationTarget.FUNCTION)
    @Retention(AnnotationRetention.RUNTIME)
    @MapKey
    internal annotation class ViewModelKey(val value: KClass<out ViewModel>)
    
    @Scope
    @Retention(AnnotationRetention.RUNTIME)
    annotation class AppScope
    
    @Qualifier
    @Retention(AnnotationRetention.RUNTIME)
    annotation class ApplicationContext
    
    class HomeActivity : AppCompatActivity(), HasSupportFragmentInjector{
    
        @Inject lateinit var androidInjector: DispatchingAndroidInjector<Fragment>
        override fun supportFragmentInjector() = androidInjector
    
        override fun onCreate(savedInstanceState: Bundle?) {
            AndroidInjection.inject(this)
            super.onCreate(savedInstanceState) 
            ...
        }
    
        fun openHomeFragment() {
            //to make a quick experiment
            supportFragmentManager.beginTransaction()
                    .replace(R.id.fragmentLayout, HomeFragment(),"HomeFragment")
                    .addToBackStack(null)
                    .commit();
        }
    
    }
    
    class HomeFragment : Fragment(){
    
        @Inject lateinit var viewModelFactory: ViewModelFactory
    
        //@Inject
        val viewModel by lazy { ViewModelProviders.of(this, viewModelFactory).get(HomeFragmentViewModel::class.java) }
    
        override fun onAttach(context: Context?) {
            AndroidSupportInjection.inject(this)
            super.onAttach(context)
        }
    
    }
    
    class HomeFragmentViewModel() : ViewModel()
    
    //project.gradle
    apply plugin: 'com.android.application'
    apply plugin: 'kotlin-android'
    apply plugin: 'kotlin-kapt'
    apply plugin: 'kotlin-android-extensions'
    
    buildscript {
        repositories {
            jcenter()
            mavenCentral()
        }
        dependencies {
            classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
            classpath "org.jetbrains.kotlin:kotlin-android-extensions:$kotlin_version"
        }
    }
    
    android {
        compileSdkVersion 27
        buildToolsVersion '26.0.2'
    
        defaultConfig {
            minSdkVersion 16
            targetSdkVersion 27
    
            applicationId "kibar.app"
            versionCode 1130
            versionName '1.1.0'
            archivesBaseName = "app-$versionName-$versionCode"
            multiDexEnabled true
    
            javaCompileOptions {
                annotationProcessorOptions {
                    includeCompileClasspath = true
                }
            }
        }
    
        buildTypes {
            release {
                debuggable false
                minifyEnabled true
                shrinkResources true
                proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
            }
    
            debug {
                debuggable true
                proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
            }
        }
    
        compileOptions {
            sourceCompatibility JavaVersion.VERSION_1_8
            targetCompatibility JavaVersion.VERSION_1_8
        }
    
        packagingOptions {
            exclude 'META-INF/rxjava.properties'
        }
    
        sourceSets {
            main.java.srcDirs += 'src/main/kotlin'
        }
    }
    
    kapt {
        generateStubs = true
    }
    
    dependencies {
        implementation fileTree(include: ['*.jar'], dir: 'libs')
        implementation 'com.android.support:multidex:1.0.2'
        implementation "com.android.support:design:$android_support_version"
        implementation "com.android.support:appcompat-v7:$android_support_version"
        implementation "com.android.support:recyclerview-v7:$android_support_version"
        implementation "com.android.support:cardview-v7:$android_support_version"
        implementation "com.google.firebase:firebase-ads:$firebase_version"
        implementation "com.google.firebase:firebase-crash:$firebase_version"
        implementation "com.google.firebase:firebase-config:$firebase_version"
        implementation 'com.google.code.gson:gson:2.8.2'
        implementation 'com.jakewharton.timber:timber:4.6.0'
        implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
        implementation 'com.github.clans:fab:1.6.4'
        implementation 'com.squareup.retrofit2:retrofit:2.3.0'
        implementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
        implementation "android.arch.persistence.room:runtime:$room_version"
        kapt "android.arch.persistence.room:compiler:$room_version"
    
        implementation "android.arch.lifecycle:runtime:1.0.3"
        implementation "android.arch.lifecycle:extensions:1.0.0-rc1"
        implementation "android.arch.lifecycle:reactivestreams:1.0.0-rc1"
        kapt "android.arch.lifecycle:compiler:1.0.0-rc1"
    
        implementation "com.google.dagger:dagger:$dagger_version"
        implementation "com.google.dagger:dagger-android:$dagger_version"
        implementation "com.google.dagger:dagger-android-support:$dagger_version"
        kapt "com.google.dagger:dagger-android-processor:$dagger_version"
        kapt "com.google.dagger:dagger-compiler:$dagger_version"
        provided 'org.glassfish:javax.annotation:10.0-b28'
    }
    
    apply plugin: 'com.google.gms.google-services'
    
    //top.gradle
    buildscript {
        ext{
            android_support_version = "27.0.0"
            kotlin_version          = "1.1.51"
            firebase_version        = "11.4.2"
            room_version            = "1.0.0-alpha9-1"
            dagger_version          = "2.12"
        }
    
        repositories {
            jcenter()
            maven { url "https://maven.google.com" }
            maven { url "https://jitpack.io" }
        }
    
        dependencies {
            classpath 'com.android.tools.build:gradle:3.0.0'
            classpath 'com.google.gms:google-services:3.1.1'
        }
    }
    
    allprojects {
        repositories {
            jcenter()
            maven { url "https://maven.google.com" }
            maven { url "https://jitpack.io" }
        }
    
        gradle.projectsEvaluated {
            tasks.withType(JavaCompile) {
                options.compilerArgs << "-Xmaxerrs" << "1000"
            }
        }
    }
    

    你知道怎么解决这个问题吗?

    2 回复  |  直到 7 年前
        1
  •  2
  •   savepopulation    7 年前

    您没有 inject 带注释的构造函数或 Provides 带注释的方法,例如 Module 为了您的 HomeFragmentViewModel . 所以你得到了这个错误。

    class HomeFragmentViewModel @Inject constructor(): ViewModel()
    

    我想这对你有帮助。

        2
  •  2
  •   tynn    7 年前

    就像错误状态一样

    kibar.app.ui.fragment.HomeFragmentViewModel cannot be provided without an @Inject constructor or from an @Provides-annotated method.
    

    HomeFragmentViewModel 以某种方式你可以在一个 @Module 或者使用可注入构造函数

    class HomeFragmentViewModel @Inject constructor()