简体   繁体   中英

Android + Kotlin + Hilt: Cannot @Inject in a non activity class

I recently started learning Hilt, and for now it's giving me more headaches than happiness, but I like challenges, so let's go to the point.

I'm having plenty of problems understanding how to inject in non activity classes, for example to inject a Context or any other class.

Most of my non activity classes were objects (singleton) so far, but as I wasn't able to inject into an object I decided to convert them to classes.

I'm trying to inject with field injection, because if I use constructor injection with @AndroidEntryPoint it complains that class must inherit from AppCompatActivity or so...

Let's put a simple example of my issue.

My app is a multi-module app and I have a AWFile class in the "Common" module (accesible from all other modules [I'm talking about Gradle modules, not Hilt modules now]) that carries file operations, so to start testing injection with Hilt I've created a method doSomething and, in doSomething I'm trying to get access to appSettings , a class where I hold all app configuration that is injected through field injection, but even the app builds fine I always get null -an error in fact- in the fields (I'm not sure how to get the context also).

AWApplication:

@HiltAndroidApp
class AWApplication  : MultiDexApplication() {

}

AWFile:

class AWFile @Inject constructor() {

    @Inject
    lateinit var app: AppSettings

    @Inject
    lateinit var context: Context

        fun doSomething(){
            var color = app.actionBarEndColor
            var ctx = context
        }
        ...
}

Dependencies (in app module):

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

    @Provides
    @ApplicationContext
    fun provideContext(@ApplicationContext appContext: Context): Context {
        return appContext
    }
}

Dependencies in "Common" project module:

@Module
@InstallIn(SingletonComponent::class)
class Dependencies {
    @ApplicationContext
    lateinit var appContext: Context

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

    @Provides
    @Singleton
    fun bindsAWFile(): AWFile {
        return AWFile()
    }
}

MainActivity:

@AndroidEntryPoint
class MainActivity : FragmentActivity() {
    @Inject
    lateinit var awFile: AWFile

    @Inject
    lateinit var app: AppSettings

    override fun onCreate(savedInstanceState: Bundle?) {
        requestWindowFeature(Window.FEATURE_CUSTOM_TITLE)
        super.onCreate(savedInstanceState)

        awFile.doSomething()
    }

The error when debugging into AWFile > doSomething:

kotlin.UninitializedPropertyAccessException: lateinit property app has not been initialized (the same for context)

Edit 1: Trying to do constructor injection (instead of field injection) in AWFile as @CommonsWare suggested.

AWFile declaration:

class AWFile @Inject constructor(private var app: AppSettings, private var context: Context)

"Common" module > Dependencies:

@Module
@InstallIn(SingletonComponent::class)
class Dependencies {
    @ApplicationContext
    lateinit var appContext: Context

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

    @Provides
    @Singleton
    fun bindsAWFile(): AWFile {
        return AWFile()
    }
}

Result:

The application does not builds. It complains that not parameter was passed for app in bindsAWFile() > AWFile (the method I use to provide AWFile) and I don't know how to pass it:s

Edit 2: Still following @CommonsWare suggestions.

Dependencies:

@Module
@InstallIn(SingletonComponent::class)
class Dependencies {
    @ApplicationContext
    lateinit var appContext: Context

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

    @Provides
    @Singleton
    fun bindsAWFile(appSettings: AppSettings): AWFile {
        return AWFile(appSettings, appContext)
    }
}

Result:

...Caused by: kotlin.UninitializedPropertyAccessException: lateinit property appContext has not been initialized
        at dependencies.Dependencies.getAppContext(Dependencies.kt:18)...

Edit 3: Resolved.

App Dependencies class:

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

    @Provides
    @Singleton
    fun provideContext(@ApplicationContext appContext: Context): Context {
        return appContext
    }
}

"Commons" module Dependencies class:

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

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

    @Provides
    @Singleton
    fun bindsAWFile(appSettings: AppSettings, context: Context): AWFile {
        return AWFile(appSettings, context)
    }
}

Now it's working, and the keys were first to provide a Context in app Dependencies module and second to include appSettings and context into bindsAWFile method declaration as parameters that then will be passed to AWFile as @CommonsWare suggested.

Hilt is being a nightmare, but thanks for your time @CommonsWare.

Ok, after struggling my head for hours I got a solution.

App Dependencies class:

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

    @Provides
    @Singleton
    fun provideContext(@ApplicationContext appContext: Context): Context {
        return appContext
    }
}

"Commons" module Dependencies class:

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

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

    @Provides
    @Singleton
    fun bindsAWFile(appSettings: AppSettings, context: Context): AWFile {
        return AWFile(appSettings, context)
    }
}

The rest of code keeps intact.

Now it's working, and the keys were first to provide a Context in app Dependencies module and second to include AppSettings and context into bindsAWFile method declaration as parameters that then will be passed to AWFile as @CommonsWare suggested.

Notice the important of the @ApplicationContext .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM