BareBonesAndroidDagger icon indicating copy to clipboard operation
BareBonesAndroidDagger copied to clipboard

Bare-bones implementation of android-dagger

Bare-Bones Android Dagger

This is a simple app to demonstrate the use of the following technologies together:

  • Kotlin 1.1.4
  • Dagger2 2.12 with the android-dagger extension
  • Android Studio 3 beta 7
  • Gradle 3.0.0 beta 7
  • Inherited activities provide custom dependencies
  • Base activities provide common dependencies

The idea is to make a simple app that allows for custom activity hierarchy.

The hierarchy goes as follows:

  • Basic Level: Things that are shared dependencies for all activities, fragments and for the custom Application class
  • Custom Level: Things that are custom dependencies for a specific activity or fragment or a set of those two types.

Class Breakdown: ApplicationComponent

@Singleton
@Component(modules = arrayOf(
        AndroidInjectionModule::class,
        ActivityBuilder::class,
        ApplicationModule::class
))
interface ApplicationComponent : AndroidInjector<Application> {
    fun inject(app: MyApplication)
}
  

Here, you will find the basic necessary modules for an Application component. AndroidInjectionModule is part of the new android-dagger extension.

Class Breakdown: ApplicationModule

@Module
class ApplicationModule {
    @Provides
    @Singleton
    fun providesCommonStuff() = Db()

    @Provides
    @Singleton
    fun providesApi() = Api()
}

This is the Basic Level of the dependency hierarchy identified above, namely the dependencies that would be shared by all activities, fragments, and application class.

Class Breakdown: MainActivityModule

@Module
class MainActivityModule {
    @Provides
    fun provideAudioPlayer(activity: MainActivity): AudioPlayer = AudioPlayer(activity)
}  

This is the Custom Level of the dependency hierarchy identified above, namely the dependencies that would be used by a specific activity or a set of activities (or fragments). In this case, MainActivityModule is specific to MainActivity.

Class Breakdown: ActivityBuilder

@Module
abstract class ActivityBuilder {
    @ActivityScope
    @ContributesAndroidInjector(modules = arrayOf(MainActivityModule::class))
    abstract fun providesMainActivityInjector(): MainActivity

    @ActivityScope
    @ContributesAndroidInjector
    abstract fun providesOtherActivityInjector(): OtherActivity
}

ActivityBuilder is responsible for designating all activities (or fragments) that would need to be injected. Notice that you can add a custom module, which we did in the case of providesMainActivityInjector(). This is not necessary, of course. OtherActivity, for instance, doesn't need any dependencies other than the basic ones it gets by default from ApplicationModule, so there is no need for a special Module for it.

Class Breakdown: MainActivity

class MainActivity : BaseActivity() {
    @Inject lateinit var api: Api
    @Inject lateinit var db: Db
    @Inject lateinit var audioPlayer: AudioPlayer

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

        btn_run_api.let {
            it.setOnClickListener {
                api.validateUser()
            }
        }

        btn_run_db.let {
            it.setOnClickListener {
                db.fetchData()
            }
        }

        btn_run_audio_player.let {
            it.setOnClickListener {
                audioPlayer.doToast()
            }
        }

        btn_goto_next_activity.let {
            it.setOnClickListener {
                this.startActivity(Intent(this, OtherActivity::class.java))
            }
        }

    }
}

MainActivity extends BaseActivity (see below). The Dagger injection happens in BaseActivity, so MainActivity doesn't need to worry about that part. It just adds an @Inject lateinit var to whatever component needs to be injected and the structure we've built will take care of the rest.

Notice here that the two dependencies, api: Api and db: Db are common dependencies following the Basic Level of the dependency hierarchy. They are provided using ApplicationModule.

In contrast, there is a third dependency that is special only for MainActivity, which is audioPlayer: AudioPlayer. This dependency is provided by MainActivityModule.

Class Breakdown: BaseActivity

abstract class BaseActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        AndroidInjection.inject(this)

        super.onCreate(savedInstanceState)
    }
}

As mentioned above, BaseActivity is responsible for handling the Dagger injection. No need for any special overrides to the onCreate() in the inheritance chain.

Class Breakdown: MyApplication

class MyApplication : Application(),
        HasActivityInjector {
    @Inject lateinit var activityInjector: DispatchingAndroidInjector<Activity>
    @Inject lateinit var db: Db

    override fun onCreate() {
        super.onCreate()
        DaggerApplicationComponent
                .create()
                .inject(this)

        initDb()
    }

    override fun activityInjector(): AndroidInjector<Activity> {
        return activityInjector
    }

    private fun initDb() {
        db.initDb()
    }
}

So MyApplication has a few extra things from your regular vanilla Dagger2 implementation. Notice the interface implementation of HasActivityInjector. This setup is basic boilerplate for having android-dagger initialized. If you get errors regarding a missing DaggerApplicationComponent, just rebuild the project and make sure your build.gradle includes the necessary kapt dependencies (see project's build.gradle).