1

I'm new using Dagger2 (I always used Koin) and I'm trying to implement a simple sample but I don't really know what I'm missing. This is what I got so far.

app.gradle:

ext.daggerVersion = '2.23.2'

implementation "com.google.dagger:dagger:$daggerVersion"
implementation "com.google.dagger:dagger-android-support:$daggerVersion"
kapt "com.google.dagger:dagger-android-processor:$daggerVersion"
kapt "com.google.dagger:dagger-compiler:$daggerVersion"

AppModule.kt:

@Module
class AppModule {
    @Provides
    @Singleton
    fun provideApplication(app: App): Application = app

    @Provides
    @Singleton
    fun provideTestOperator(testOperator: TestOperator) = testOperator

    @Provides
    @Singleton
    fun provideTestClass(testClass: TestClass) = testClass

}

AppComponent.kt:

@Singleton
@Component(modules = [
    AndroidInjectionModule::class,
    AppModule::class
])
interface AppComponent : AndroidInjector<App> {
    @Component.Builder
    interface Builder {
        @BindsInstance
        fun application(app: App): Builder
        fun build(): AppComponent
    }
}

TestClass.kt & TestOperator.kt in the same file:

class TestClass @Inject constructor(private val testOperator: TestOperator) {
    fun getRandomValueFromCTest(): Int = testOperator.generateRandomNumber()
}

class TestOperator @Inject constructor() {
    fun generateRandomNumber(): Int = Random.nextInt()
}

App.kt:

class App : DaggerApplication() {
    override fun applicationInjector(): AndroidInjector<out DaggerApplication> {
        return DaggerAppComponent.builder().application(this@App).build()
    }
}

MainActivity.kt:

class MainActivity : AppCompatActivity() {
    @Inject
    lateinit var testClass: TestClass

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }

    override fun onResume() {
        super.onResume()
        val x = testClass.getRandomValueFromCTest()
    }
}

Error: testClass == null

denvercoder9
  • 2,979
  • 3
  • 28
  • 41
pawer
  • 43
  • 7

3 Answers3

3

AppModule.kt: Provide the application context. No need to write @singleton @provides for your Test* classes (will see why)

@Module
class AppModule {
    @Provides
    @Singleton
    fun provideApplication(app: App): Context = app.applicationContext
}

AppComponent.kt: @Component.Builder is deprecated IIRC. Use @Component.Factory. And replace AndroidInjectionModule::class with AndroidSupportInjectionModule::class since we are using dagger-android-support and android's *Compat* stuff. Refer a new module here called ActivityModule::class.

@Singleton
@Component(modules = [
    ActivityModule::class
    AndroidSupportInjectionModule::class,
    AppModule::class
])
interface AppComponent : AndroidInjector<App> {

    @Component.Factory
    abstract class Factory : AndroidInjector.Factory<App>
}

TestClass.kt & TestOperator.kt: Since you were providing singletons by writing @singleton and @provides method, I assume you want them to be singletons. Just annotate the class definition with @Singleton and dagger will take care of it. No need to write @Provides methods.

@Singleton
class TestClass @Inject constructor(private val testOperator: TestOperator) {
    fun getRandomValueFromCTest(): Int = testOperator.generateRandomNumber()
}

@Singleton
class TestOperator @Inject constructor() {
    fun generateRandomNumber(): Int = Random.nextInt()
}

App.kt: Using factory instead of builder since @Component.Builder is deprecated.

class App : DaggerApplication() {
    override fun applicationInjector(): AndroidInjector<out DaggerApplication> {
        return DaggerAppComponent.factory().create(this)
    }
}

ActivityModule.kt: Provide a module to dagger to create your activities.

@Module
interface ActivityModule {

    @ContributesAndroidInjector
    fun provideMainActivity(): MainActivity
}

MainActivity.kt: Finally, extend from DaggerAppCompatActivity.

class MainActivity : DaggerAppCompatActivity() {

    @Inject
    lateinit var testClass: TestClass

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }

    override fun onResume() {
        super.onResume()
        val x = testClass.getRandomValueFromCTest()
    }
}

I believe this should run without issues. For more reference you could look into this sample and the new simpler docs at dagger.dev/android

denvercoder9
  • 2,979
  • 3
  • 28
  • 41
2

You are missing the actual injection call.

class MainActivity : AppCompatActivity() {
  @Inject
  lateinit var testClass: TestClass

  override fun onCreate(savedInstanceState: Bundle?) {
      AndroidInjection.inject(this)
      super.onCreate(savedInstanceState)
      setContentView(R.layout.activity_main)
  }
EpicPandaForce
  • 79,669
  • 27
  • 256
  • 428
0

MainActivity should extends DaggerActivity, not AppCompatActivity

Romadro
  • 626
  • 4
  • 10
  • 2
    It's not true, actually. It may extend `DaggerActivity` but it is not required. It's enough to call `AndroidInjection.inject(/* ... */)` in `onCreate(/* ... */)` method. – Tomasz Dzieniak Sep 22 '19 at 08:44