Kotlin & Dagger2

The project is based on the code complete in last article. You can download from here

Add Dependencies to your build.gradle

 kapt 'com.google.dagger:dagger-compiler:2.9'
 compile 'com.google.dagger:dagger:2.9'

Create your application instead of default

class App : Application() {
    override fun onCreate() {
    }
}

Update your manifest file to enable your App

Add the following to your application tag

     <application
+        android:name=".App"
         android:allowBackup="true"
         android:icon="@mipmap/ic_launcher"
         android:label="@string/app_name"

Create Application module

Create a AppModule.kt file and add the following code

@Module
class AppModule(val app: App) {

    @Provides
    @Singleton
    fun app(): App {
        return app
    }

    @Provides
    @Singleton
    fun context(): Context {
        return app
    }

    @Provides
    @Singleton
    fun connectivity(): ConnectivityManager {
        val service = app.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
        return service
    }
}

Create component

@Singleton
@Component(
        modules = arrayOf(AppModule::class)
)
interface AppComponent {
    fun inject(app: App)
}

Update your code in App.kt to enable the injection

  lateinit var component: AppComponent
    lateinit @Inject var cm: ConnectivityManager
    override fun onCreate() {
        super.onCreate()
        component = DaggerAppComponent.builder().appModule(AppModule(this)).build()
        component.inject(this)
        Logger.d("connectivity manager : ${cm}")
    }

Now you can run your code in IDE, you will see the following in your console. You have completed your first injection for Application.

02-17 10:03:20.492 10572-10572/? D/PRETTYLOGGER: ╔════════════════════════════════════════════════════════════════════════════════════════
02-17 10:03:20.493 10572-10572/? D/PRETTYLOGGER: ║ Thread: main
02-17 10:03:20.493 10572-10572/? D/PRETTYLOGGER: ╟────────────────────────────────────────────────────────────────────────────────────────
02-17 10:03:20.493 10572-10572/? D/PRETTYLOGGER: ║ BootstrapApplication.onCreate  (BootstrapApplication.java:370)
02-17 10:03:20.493 10572-10572/? D/PRETTYLOGGER: ║    App.onCreate  (App.kt:21)
02-17 10:03:20.493 10572-10572/? D/PRETTYLOGGER: ╟────────────────────────────────────────────────────────────────────────────────────────
02-17 10:03:20.493 10572-10572/? D/PRETTYLOGGER: ║ connectivity manager : android.net.ConnectivityManager@39309b5
02-17 10:03:20.494 10572-10572/? D/PRETTYLOGGER: ╚════════════════════════════════════════════════════════════════════════════════════════

Follow above guide to create ActivityModule and ActivityComponent

@Module
class ActivityModule(val activity: DaggerActivity) {

    @Provides
    fun activity(): DaggerActivity {
        return activity
    }

    @Provides
    fun progressDialog(): ProgressDialog {
        val progress = ProgressDialog(activity)
        progress.setMessage(activity.getString(R.string.loading))
        return progress
    }
}
@ForActivity
@Component(
        modules = arrayOf(ActivityModule::class),
        dependencies = arrayOf(AppComponent::class)
)
interface ActivityComponent {
    fun inject(activity: MainActivity)
}

You definately find there’s a @ForActivity above the ActivityComponent. Dagger need you to set a scope to define the lifecycle for any component. Activity cannot be the singleton. So we define a scrope annotation called ‘ForActivity’.

@Qualifier
@Retention(value = AnnotationRetention.RUNTIME)
@Scope
annotation class ForActivity

Use activity component in your code

Create a base class called DaggerActivity

abstract class DaggerActivity : AppCompatActivity() {
    var component: ActivityComponent? = null
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val app = application as App
        component = DaggerActivityComponent.builder()
                .activityModule(ActivityModule(this))
                .appComponent(app.component)
                .build()
        onInject()
    }

    abstract fun onInject()

    override fun onDestroy() {
        component = null
        super.onDestroy()
    }
}

Update your MainActivity to extends DaggerActivity

class MainActivity : DaggerActivity() {
    @Inject lateinit var apiService: ApiService
    @Inject lateinit var cm: ConnectivityManager
    @Inject lateinit var progressDialog: ProgressDialog

    private val subscriptions = CompositeSubscription()

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

        val binding = DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
        binding.greating = "Hello Kotlin Data Binding!!!"

        val toolbar = findViewById(R.id.toolbar) as Toolbar
        setSupportActionBar(toolbar)

        Logger.d(cm)
    }

    override fun onInject() {
        component?.inject(this) // must call this
    }


    override fun onResume() {
        super.onResume()
        progressDialog.show()
    }

    override fun onStart() {
        super.onStart()
        subscriptions.add(apiService.ip().subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe({
                    ip ->
                    Logger.d("get result $ip")
                    progressDialog.hide()
                }) { error ->
                    Logger.e(error, "Aha.. got error message")
                    progressDialog.hide()
                })
    }

    override fun onStop() {
        super.onStop()
        subscriptions.clear()
    }

}

Run code from you IDE, you will see the fowlloing error

Error:mingsin.androidkotlinexample.data.ApiService cannot be provided without an @Provides-annotated method.
Error:mingsin.androidkotlinexample.data.ApiService cannot be provided without an @Provides- or @Produces-annotated method.
Error:android.net.ConnectivityManager cannot be provided without an @Inject constructor or from an @Provides- or @Produces-annotated method.

Because ConnectivityManager and ApiService are provided in you app component, So we must tell the dagger that we provide the two instance. Update your AppComponent code as following:

@Singleton
@Component(
        modules = arrayOf(AppModule::class)
)
interface AppComponent {
    fun inject(app: App)

    fun connectivity(): ConnectivityManager
    fun api(): ApiService
}

Now you have complete a project integrated with dagger2. You can chekcout the whole project from here

References

  1. Dagger2
  2. Dagger 2 Dependency Injection for Android Developers
  3. Dependency Injection with Dagger 2