android – error: [Dagger/MissingBinding] Map<Class<? extends ViewModel>, Provider<ViewModel>> cannot be provided without an @Provides-annotated method-ThrowExceptions

Exception or error:

I have the problem with Dagger/MissingBinding. I have red all the related answers on stackoverflow, I tried to use different versins of kotlin, gradle, dagger, I tried to use different workarounds such as kapt { correctErrorTypes = true } and kapt { generateStubs = true }, but the problem still go on.

Error looks like:

E:\AndroidStudioPrjcts\windygo\app\src\main\java\app\windygo\dagger\AppComponent.java:12: error: [Dagger/MissingBinding] java.util.Map<java.lang.Class<? extends androidx.lifecycle.ViewModel>,javax.inject.Provider<androidx.lifecycle.ViewModel>> cannot be provided without an @Provides-annotated method.
public interface AppComponent {
       ^
      java.util.Map<java.lang.Class<? extends androidx.lifecycle.ViewModel>,javax.inject.Provider<androidx.lifecycle.ViewModel>> is injected at
          app.windygo.ui.DefaultViewModelFactory(creators)
      app.windygo.ui.DefaultViewModelFactory is injected at
          app.windygo.dagger.ViewModelModule.bindDefaultViewModelFactory(factory)
      androidx.lifecycle.ViewModelProvider.Factory is injected at
          app.windygo.dagger.DaggerWrapper.factory
      app.windygo.dagger.DaggerWrapper is injected at
          app.windygo.dagger.AppComponent.inject(app.windygo.dagger.DaggerWrapper)

And my sources:

AppComponent.java

package app.windygo.dagger;

import android.content.Context;

import javax.inject.Singleton;

import dagger.BindsInstance;
import dagger.Component;

@Singleton
@Component(modules = { AppModule.class, ViewModelModule.class })
public interface AppComponent {

    @Component.Builder
    interface Builder {
        @BindsInstance Builder context(Context context);
        AppComponent build();
    }

    void inject(DaggerWrapper wrapper);
}

AppModule.java

package app.windygo.dagger;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

import javax.inject.Singleton;

import app.windygo.BuildConfig;
import app.windygo.api.LiveDataCallAdapterFactory;
import app.windygo.api.WindyGoSource;
import dagger.Module;
import dagger.Provides;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

@Module
public class AppModule {
    @Singleton
    @Provides
    HttpLoggingInterceptor getInterceptor() {
        return new HttpLoggingInterceptor(/*new ApiLogger()*/);
    }

    @Singleton
    @Provides
    HttpLoggingInterceptor.Level getLogLevel() {
        return BuildConfig.DEBUG ? HttpLoggingInterceptor.Level.BODY : HttpLoggingInterceptor.Level.NONE;
    }

    @Singleton
    @Provides
    WindyGoSource provideGoodwineService() {
        OkHttpClient.Builder builder = new OkHttpClient.Builder();
        builder.addInterceptor(chain -> {
            Request original = chain.request();

            String token = original.header(WindyGoSource.HEADER_AUTHORIZATION);
            if (token != null) {
                Request.Builder requestBuilder = original.newBuilder()
                        .header("Authorization", "Bearer " + token);

                original = requestBuilder.build();
            }
            return chain.proceed(original);
        });
        builder.addNetworkInterceptor(getInterceptor().setLevel(getLogLevel()));
        OkHttpClient client = builder.build();

        Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").create();

        return new Retrofit.Builder()
                .baseUrl(BuildConfig.API_URL)
                .addConverterFactory(GsonConverterFactory.create(gson))
                .addCallAdapterFactory(new LiveDataCallAdapterFactory())
                .client(client)
                .build()
                .create(WindyGoSource.class);
    }

}

DaggerWrapper.java

package app.windygo.dagger;

import androidx.lifecycle.ViewModelProvider;

import javax.inject.Inject;

public class DaggerWrapper {

    @Inject
    ViewModelProvider.Factory factory;

    public ViewModelProvider.Factory getViewModelFactory() {
        return factory;
    }
}

ViewModelKey.java

package app.windygo.dagger;

import androidx.lifecycle.ViewModel;

import dagger.MapKey;

@MapKey
@interface ViewModelKey {
    Class<? extends ViewModel> value();
}

ViewModelModule.java

package app.windygo.dagger;

import androidx.lifecycle.ViewModelProvider;

import app.windygo.ui.DefaultViewModelFactory;
import dagger.Binds;
import dagger.Module;

@Module
public abstract class ViewModelModule {

    @Binds
    public abstract ViewModelProvider.Factory bindDefaultViewModelFactory(DefaultViewModelFactory factory);

}

DefaultViewModelFactory.java

package app.windygo.ui;

import androidx.lifecycle.ViewModel;
import androidx.lifecycle.ViewModelProvider;
import androidx.annotation.NonNull;

import java.util.Map;

import javax.inject.Inject;
import javax.inject.Provider;
import javax.inject.Singleton;

@Singleton
public class DefaultViewModelFactory implements ViewModelProvider.Factory {

    private final Map<Class<? extends ViewModel>, Provider<ViewModel>> creators;

    @Inject
    public DefaultViewModelFactory(Map<Class<? extends ViewModel>, Provider<ViewModel>> creators) {
        this.creators = creators;
    }

    @NonNull
    @Override
    public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
        Provider<? extends ViewModel> creator = creators.get(modelClass);
        if (creator == null) {
            throw new IllegalArgumentException("unknown model class " + modelClass);
        }

        try {
            return (T) creator.get();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

I am using java for dagger-dependent classes because of different problems with kapt and kotlin when configuring Dagger 2. But I even tried to rewrite these classes using Kotlin, but it not solved my problem.

And finally, my build.gradle files:

build.gradle (Project)

buildscript {
    ext.kotlin_version = '1.3.61'

    repositories {
        google()
        jcenter()

    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.5.3'
        classpath 'com.google.gms:google-services:4.3.3'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
        classpath "org.jetbrains.kotlin:kotlin-android-extensions:$kotlin_version"
    }
}

allprojects {
    repositories {
        google()
        jcenter()
        maven { url 'https://jitpack.io' }
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

build.gradle (app)

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'com.google.gms.google-services'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'

def versionMajor = 1
def versionMinor = 0 // 0..9
def versionPatch = 0 // 0..9
def versionBuild = 0 // 0..99

static def getGitRevParseInfo(what) {
    def cmd = "git rev-parse " + what + " HEAD"
    def proc = cmd.execute()
    proc.text.trim()
}

android {
    compileSdkVersion 29
    defaultConfig {
        multiDexEnabled true
        applicationId "app.windygo"
        minSdkVersion 23
        targetSdkVersion 29
        versionCode versionMajor * 10000 + versionMinor * 1000 + versionPatch * 100 + versionBuild
        versionName "${versionMajor}.${versionMinor}.${versionPatch}.${versionBuild}"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
        debug {
            debuggable true
        }
    }
    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_1_8
        targetCompatibility = JavaVersion.VERSION_1_8
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'androidx.core:core-ktx:1.2.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    testImplementation 'junit:junit:4.13'
    androidTestImplementation 'androidx.test.ext:junit:1.1.1'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
    // Dagger 2
    kapt "com.google.dagger:dagger-compiler:2.26"
    implementation "com.google.dagger:dagger:2.26"
    //Retrofit
    implementation 'com.squareup.retrofit2:retrofit:2.7.1'
    implementation 'com.squareup.retrofit2:converter-gson:2.7.1'
    implementation 'com.squareup.okhttp3:logging-interceptor:4.4.0'
    // ViewModel and LiveData
    implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
    implementation 'androidx.lifecycle:lifecycle-common-java8:2.2.0'
}

So, please tell me any ideas how to fix it?

How to solve:

Without binding at least one ViewModel with ViewModelKey, dagger doesn’t create function that provides Map<Class<? extends ViewModel>, Provider<ViewModel>>

Leave a Reply

Your email address will not be published. Required fields are marked *