Home > OS >  Android Kotlin Hilt: Activity has no zero argument constructor
Android Kotlin Hilt: Activity has no zero argument constructor

Time:07-30

I'm working on a multi-module app with Kotlin and Hilt, and my MainActivity crashes when starting with the following exception:

2022-07-29 09:32:00.547 23850-23850/com.myApp.myApp E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.myApp.myApp, PID: 23850
java.lang.RuntimeException: Unable to instantiate activity ComponentInfo{com.myApp.myApp/com.myApp.myApp.activities.main.MainActivity}: java.lang.InstantiationException: java.lang.Class<com.myApp.myApp.activities.main.MainActivity> has no zero argument constructor
    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3365)
    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3601)
    at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85)
    at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
    at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2066)
    at android.os.Handler.dispatchMessage(Handler.java:106)
    at android.os.Looper.loop(Looper.java:223)
    at android.app.ActivityThread.main(ActivityThread.java:7656)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
 Caused by: java.lang.InstantiationException: java.lang.Class<com.myApp.myApp.activities.main.MainActivity> has no zero argument constructor
    at java.lang.Class.newInstance(Native Method)
    at android.app.AppComponentFactory.instantiateActivity(AppComponentFactory.java:95)
    at androidx.core.app.CoreComponentFactory.instantiateActivity(CoreComponentFactory.java:45)
    at android.app.Instrumentation.newActivity(Instrumentation.java:1253)
    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3353)
    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3601) 
    at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85) 
    at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135) 
    at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95) 
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2066) 
    at android.os.Handler.dispatchMessage(Handler.java:106) 
    at android.os.Looper.loop(Looper.java:223) 
    at android.app.ActivityThread.main(ActivityThread.java:7656) 
    at java.lang.reflect.Method.invoke(Native Method) 
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592) 
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)

MainActivity:

@AndroidEntryPoint
class MainActivity
@Inject constructor(
    private var awLocale: AWLocale,
    private var awUtils: AWUtils,
    private var awDate: AWDate,
    private var awPreferences: AWPreferences) :
    FragmentActivity(),
    IActionListeners, IImageListeners, OnListFragmentInteractionListener
{
    private var mainViewModel: MainViewModel = ViewModelProvider(this).get(MainViewModel::class.java)
    ...Body
}

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.myApp.myApp'
        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 = 'myApp' }
                    def newName = 'myApp_'   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.myApp.myApp'
}

// 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.1'
    //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')

    kapt 'androidx.hilt:hilt-compiler:1.0.0'

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

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

Application class:

@HiltAndroidApp
class AWApplication  : MultiDexApplication() {

}

Provider module:

@Module
@InstallIn(SingletonComponent::class)
class Dependencies {

    @Provides
    @Singleton
    fun bindsAppSettings(): AppSettings {
        return AppSettings()
    }

    @Provides
    @Singleton
    fun bindsAWFile(appSettings: AppSettings, context: Context,
                    awException: ExceptionHandler, awPermission: AWPermission, awUtils: AWUtils): AWFile {
        return AWFile(appSettings, context, awException, awPermission, awUtils)
    }

    @Provides
    @Singleton
    fun bindsAWLocale(appSettings: AppSettings, context: Context): AWLocale {
        return AWLocale(appSettings, context)
    }

    @Provides
    @Singleton
    fun bindsAWDate(awLocale: AWLocale, awUtils: AWUtils): AWDate {
        return AWDate(awLocale, awUtils)
    }

    @Provides
    @Singleton
    fun bindsAWException(appSettings: AppSettings, context: Context,
                         awDate: AWDate, awJson: AWJson, awEmail: AWEmail,
                         awCryptography: Cryptography, awPreferences: AWPreferences): ExceptionHandler {
        return ExceptionHandler(appSettings, context, awDate, awJson, awEmail,
                                    awCryptography, awPreferences)
    }

    @Provides
    @Singleton
    fun bindsAWJson(): AWJson {
        return AWJson()
    }

    @Provides
    @Singleton
    fun bindsAWEmail(app: AppSettings, context: Context,
                     //awException: ExceptionHandler,
                     awJson: AWJson,
                     awLocale: AWLocale, awPreferences: AWPreferences): AWEmail {
        return AWEmail(app, context, awJson, awLocale, awPreferences)
    }

    @Provides
    @Singleton
    fun bindsAWCryptography(app: AppSettings,
                                awPreferences: AWPreferences): Cryptography {
        return Cryptography(app, awPreferences)
    }

    @Provides
    @Singleton
    fun bindsAWUtils(appSettings: AppSettings, context: Context): AWUtils {
        return AWUtils(appSettings, context)
    }

    @Provides
    @Singleton
    fun bindsSMTP(appSettings: AppSettings, awException: ExceptionHandler,
                     awPreferences: AWPreferences): SMTP {
        return SMTP(appSettings, awException, awPreferences)
    }
}

MainViewModel:

@HiltViewModel
class MainViewModel
@Inject
constructor(
    private var context: Context,
    private var awPreferences: AWPreferences,
    private var paintingService: PaintingService,
    private var thoughtService: ThoughtService): ViewModel() {
}

Any help?

CodePudding user response:

You shouldn't inject your dependencies into Activity constructor when you don't need it. Take a look at this article, for such cases you have to create a factory.

In your case it would be enough to inject it as class-level variables (it also explained here).

@AndroidEntryPoint
class MainActivity :
    FragmentActivity(),
    IActionListeners, IImageListeners, OnListFragmentInteractionListener
{

 @Inject 
 lateinit var awLocale: AWLocale
 @Inject 
 lateinit var awUtils: AWUtils
 @Inject 
 lateinit var awDate: AWDate
 @Inject 
 lateinit var awPreferences: AWPreferences

    private var mainViewModel: MainViewModel = ViewModelProvider(this).get(MainViewModel::class.java)
    ...Body
}
  • Related