Home > Enterprise >  Android Kotlin Hilt: Error building project
Android Kotlin Hilt: Error building project

Time:07-20

I'm new to Hilt, and I'm struggling my head in order to implement it in my multi-module app. After infinite attempts the build error varies from "error a" to "error b", then "error c" and so.

This is how I've started implementing Hilt into my multi-module app:

Project build.gradle:

buildscript {
    ext.kotlin_version = '1.7.10'
    repositories {
        jcenter()
        google()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:7.2.1'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
        classpath("com.google.dagger:hilt-android-gradle-plugin:2.42")
    }
    ext.java_version = JavaVersion.VERSION_1_8
}

allprojects {
    repositories {
        jcenter()
        mavenCentral()
        maven { url "https://jitpack.io" }
        maven { url 'https://repository-achartengine.forge.cloudbees.com/snapshot/' }
        maven { url 'def androidHome = System.getenv("ANDROID_HOME")' }
        maven { url "/Home/Diego/Android/Sdk/extras/android/m2repository/" }
        google()
    }
}

app build.gradle:

plugins {
    id('dagger.hilt.android.plugin')
    id('com.android.application')
    id('kotlin-android')
    id('kotlin-kapt')
}

android {
    compileSdkVersion 32
    def code
    Properties versionProps = new Properties()
    def versionPropsFile = file('version.properties')
    if (versionPropsFile.exists())
        versionProps.load(new FileInputStream(versionPropsFile))
    code = (versionProps['VERSION_CODE'] ?: "0").toInteger()   1
    versionProps['VERSION_CODE'] = code.toString()
    versionProps.store(versionPropsFile.newWriter(), null)
    packagingOptions {
        resources {
            pickFirsts  = ['META-INF/LICENSE.txt']
            excludes  = ['META-INF/NOTICE.md', 'META-INF/LICENSE.md', 'META-INF/INDEX.LIST', 'META-INF/DEPENDENCIES', 'META-INF/io.netty.versions.properties']
        }
    }
    defaultConfig {
        applicationId 'com.xxx.xxx'
        minSdkVersion 23
        targetSdkVersion 32
        versionCode code
        versionName "2.0."   code
        // next ndk abifilters have to be disabled if spli apk is enabled.
        // ndk.abiFilters 'armeabi', 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'//testing
        multiDexEnabled true
        compileOptions {
            sourceCompatibility java_version
            targetCompatibility java_version
        }
    }
    compileOptions {
        sourceCompatibility java_version
        targetCompatibility java_version
    }
    splits {
        // Configures multiple APKs based on ABI.
        abi {
            // Enables building multiple APKs per ABI.
            enable true
            // By default all ABIs are included, so use reset() and include to specify that we only
            // want APKs for x86 and x86_64.
            // Resets the list of ABIs that Gradle should create APKs for to none.
            reset()
            // Specifies a list of ABIs that Gradle should create APKs for.
            include "armeabi-v7a"
            include "arm64-v8a"
            include "x86"
            include "x86_64"
            // Specifies that we do not want to also generate a universal APK that includes all ABIs.
            universalApk false
        }
    }
    buildTypes {
        release {
            /*signingConfig signingConfigs.release*/
            minifyEnabled false
            shrinkResources false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            // This next piece of code is used by apk Split
            applicationVariants.all { variant ->
                variant.outputs.all { output ->
                    project.ext { appName = 'xxx' }
                    def newName = 'xxx_'   output.getFilter(com.android.build.OutputFile.ABI)   '.apk'
                    outputFileName = new File("./", newName)
                }
                // assign different version code for each output
                variant.outputs.each { output ->
                    output.versionCodeOverride =
                            //project.ext.versionCodes.get(output.getFilter(OutputFile.ABI)) * 1000   android.defaultConfig.versionCode
                            project.ext.versionCodes.get(output.getFilter(com.android.build.OutputFile.ABI)) * 1000   code - 1000
                }
            }
        }
        debug {
        }
    }
    allprojects {
        repositories {
            jcenter()
            mavenCentral()
            def androidHome = System.getenv("ANDROID_HOME")
            maven {
                url "$androidHome/extras/android/m2repository/"
            }
            maven {
                url "https://maven.java.net/content/groups/public/"
            }
        }
        /*tasks.withType(JavaCompile) {
            options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation"
        }*/
    }
    productFlavors {
    }
    androidResources {
        ignoreAssetsPattern '!*ffprobe'
    }
    lint {
        abortOnError false
        checkReleaseBuilds false
    }
    dataBinding{
        enabled = true
    }
    namespace 'com.xxx.xxx'
}

// This next piece of code is used by apk Split
// map for the version code that gives each ABI a value. make sure to list all ABIs mentioned in splits block, an keep the order.
ext.versionCodes = ['armeabi-v7a': 1, 'arm64-v8a': 2, 'x86': 3, 'x86_64': 4]
//import com.android.build.OutputFile

dependencies {
    //P7Zip
    //implementation 'com.hzy:libp7zip:1.7.0'
    //Apache Commons
    //implementation 'commons-io:commons-io:2.6'
    //ffmpeg
    implementation 'com.arthenica:mobile-ffmpeg-min-gpl:4.2.2.LTS'
    //implementation 'com.arthenica:mobile-ffmpeg-full:4.3.1'
    //Google Guava
    api 'com.google.guava:guava:31.1-jre'
    //volley
    api 'com.android.volley:volley:1.2.1'
    //spotify
    api 'com.github.kaaes:spotify-web-api-android:0.4.1'
    //mail API 19
    //api 'com.sun.mail:android-mail:1.6.4'
    //api 'com.sun.mail:android-activation:1.6.4'
    //mail API 16
    //api 'com.sun.mail:android-mail:1.6.7'
    //api 'com.sun.mail:android-activation:1.6.7'
    //apache commons lang
    //ppppapi 'org.apache.commons:commons-lang3:3.12.0'
    implementation group: 'org.apache.commons', name: 'commons-text', version: '1.9'
    //Font Selector List Preference
    api 'com.vanniktech:vntfontlistpreference:1.0.0'
    //Rate my app
    api 'com.github.hotchemi:android-rate:1.0.1'
    //Support
    api 'androidx.appcompat:appcompat:1.4.2'
    api 'androidx.legacy:legacy-support-v4:1.0.0'
    //AlertDialog
    implementation 'com.github.d-max:spots-dialog:1.1@aar'
    //glide animated gifs
    implementation 'com.github.bumptech.glide:glide:4.13.2'
    implementation 'androidx.viewpager2:viewpager2:1.0.0'
    implementation project(path: ':Common')
    annotationProcessor 'com.github.bumptech.glide:compiler:4.13.2'
    implementation 'androidx.recyclerview:recyclerview:1.2.1'
    //preference
    implementation 'androidx.preference:preference-ktx:1.2.0'
    //
    api 'com.rockerhieu:rv-adapter-endless:1.2'
    implementation 'com.squareup.picasso:picasso:2.71828'
    //flat-dialog
    implementation 'com.github.mejdi14:Flat-Dialog-Android:1.0.5'
    //Hilt
    //implementation('com.google.dagger:hilt-android:2.40')
    //annotationProcessor('com.google.dagger:hilt-android-compiler:2.40')

    implementation 'com.google.dagger:hilt-android:2.42'
    //implementation 'androidx.navigation:navigation-fragment-ktx:2.5.0'
    //implementation 'androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha03'
    //annotationProcessor 'androidx.hilt:hilt-compiler:1.0.0'
    kapt 'com.google.dagger:hilt-android-compiler:2.42'
    kapt('org.jetbrains.kotlinx:kotlinx-metadata-jvm:0.5.0')

    implementation project(':Common')
    implementation project(':DTO')
    implementation project(':Core')
}

// Allow references to generated code
kapt {
    correctErrorTypes = true
}

Given that, I have to mention that I have an AppSettings class where I store all app configuration (now I've created AppSettings1 just for testing) and to start with something I'm trying to inject that class into the main activity so all its variables are available.

MainActivity:

@AndroidEntryPoint
class MainActivity @Inject constructor(private val appSettings: AppSettings1): FragmentActivity(), IActionListeners, IImageListeners, OnListFragmentInteractionListener
{
val color2 = appSettings.infoAlertBkgColor
...
}

MainViewModel:

@HiltViewModel
@SuppressLint("StaticFieldLeak")
class MainViewModel
@Inject constructor(@ApplicationContext var context: Context, private val appSettings: AppSettings1) : ViewModel() {
...
}

AppSettings:

@HiltAndroidApp
class AppSettings1 : MultiDexApplication() {

    val infoAlertBkgColor1 = "#60a69e"

    override fun onCreate() {
        super.onCreate()
        //instance = this
        resourses = applicationContext.resources
        outputPathCache = cacheDir.absolutePath
    }
}

And when I build the app the next error is thrown:

/Users/diego/StudioProjects/artandwords/app/build/generated/hilt/component_sources/debug/com/artandwords/thoughtoftheday/app/AppSettings1_HiltComponents.java:127: error: [Dagger/MissingBinding] com.xxx.xxx.app.AppSettings1 cannot be provided without an @Inject constructor or an @Provides-annotated method. public abstract static class SingletonC implements AppSettings1_GeneratedInjector, ^ A binding for com.xxx.xxx.app.AppSettings1 exists in com.xxx.xxx.app.AppSettings1_HiltComponents.SingletonC: com.xxx.xxx.app.AppSettings1 is injected at [com.xxx.xxx.app.AppSettings1_HiltComponents.ViewModelC] com.xxx.xxx.activities.main.MainViewModel(…, appSettings) com.xxx.xxx.activities.main.MainViewModel is injected at [com.xxx.xxx.app.AppSettings1_HiltComponents.ViewModelC] com.xxx.xxx.activities.main.MainViewModel_HiltModules.BindsModule.binds(arg0) @dagger.hilt.android.internal.lifecycle.HiltViewModelMap java.util.Map<java.lang.String,javax.inject.Provider<androidx.lifecycle.ViewModel>> is requested at [com.xxx.xxx.app.AppSettings1_HiltComponents.ViewModelC] dagger.hilt.android.internal.lifecycle.HiltViewModelFactory.ViewModelFactoriesEntryPoint.getHiltViewModelMap() [com.xxx.xxx.app.AppSettings1_HiltComponents.SingletonC → com.xxx.xxx.app.AppSettings1_HiltComponents.ActivityRetainedC → com.xxx.xxx.app.AppSettings1_HiltComponents.ViewModelC]

BTW, my app is a multi-module one, so I guess I have to install Hilt dependencies and plugin on every module (as I've already done), right?

I insist I'm new to Hilt, so it's most likely I'm doing something wrong, but cannot figure out what.

Any help?

CodePudding user response:

@AndroidEntryPoint
class MainActivity 
/*Not allowed*/
@Inject constructor(private val appSettings: AppSettings1): FragmentActivity()
...
}

First, for android classes like Activity, you can't use constructor injection for custom parameter/dependency. You can use field injection instead, if needed.

@AndroidEntryPoint
class MainActivity (): FragmentActivity(){
    //Sample field injection
    @Inject lateinit var dependencyClass: DependencyClass
..
}

Second, you don't need to inject Application class to Activity, you can access it using application or getApplication()

CodePudding user response:

Your AppSettings1 is a class which is inherited from the Application class and Hilt doesn't know what exactly that is and how to inject it - it's like an unknown entity. You have to either create a module which will tell Hilt how to coop with that (not possible in this case because you cannot remove @HiltAndroidApp) and thereby the only way is next

@HiltAndroidApp
class AppSettings1 @Inject constructor : MultiDexApplication()

After that you can inject it in your VM

@Inject constructor(@ApplicationContext var context: Context, private val app: AppSettings1) : ViewModel() {
...

But a couple of things you should think of

  1. you can get any resources you want from the applicationContext, you don't need specifically the App class itself. Your @ApplicationContext var context: Context is more than enough.
  2. retrieving resources from the app context/app is a bad idea. For example, you're switching themes and all your activities are recreated - but not the app class, so you will still have the old references of the resources and all the UI stuff will have wrong color theme.
  • Related