0

I'm trying to use new ViewModel from arch android library on my project.

I'm studied a lot of sample project on github. Finally I want to implement this architecture on my project but I can't build it.

Gradle console:

....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)
    ...
    ...
    ...

Codes:

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"
        }
    }
}

Any idea on how fix this?

kibar
  • 822
  • 3
  • 17
  • 37

2 Answers2

2

Like the error states

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

you need to provide the HomeFragmentViewModel somehow. This you can do within a @Module or with an injectable constructor

class HomeFragmentViewModel @Inject constructor()
tynn
  • 38,113
  • 8
  • 108
  • 143
2

You do not have an inject annotated constructor or a Provides annotated method such as Module for your HomeFragmentViewModel. So you get this error.

class HomeFragmentViewModel @Inject constructor(): ViewModel()

I think this'll help you.

savepopulation
  • 11,736
  • 4
  • 55
  • 80
  • Before when I saw that error, I think dagger need the HomeFragmentViewModel object then I'm add this line in the `ViewModelModule` : `@Provides fun provideViewModel() = HomeFragmentViewModel()` but I getting this error: `A @Module may not contain both non-static @Provides methods and abstract @Binds or @Multibinds declarations` Why I can't provide `HomeFragmentViewModel` object? – kibar Nov 03 '17 at 12:47