Android Dagger2: How to create and retrieve object (Kotlin)

January 21, 2019
How to Provide and Inject

This article assume you already know the basic of dagger2.

This article shall focus on

  • Provide: ways to create instances to be injected
  • Inject: how to inject instances into Activity, Fragment, Classes, Fields vs Constructor

NOTE: I am still a noob with Dagger2, so I might miss out something.

Provide / Create Object

Ways to create instances to be injected.

Instantiate Service with @Module and @Provides

This method is commonly used to instantiate singleton services/objects such as Database, Json parser (Gson/Moshi), Network/Retrofit API object, SharedPreferences, etc.

@Module
internal class AppModule {
    @Singleton
    @Provides
    fun provideDatabase(app: Application): AppDatabase {
        return Room.databaseBuilder(app,
                AppDatabase::class.java, "LuaPass.db")
                .addMigrations(MIGRATION_1_2)
                .build()
    }

    @Singleton
    @Provides
    fun provideMoshi(): Moshi {
        return Moshi.Builder()
                .add(KotlinJsonAdapterFactory())
                .add(LocalDateTimeAdapter())
                .add(MultipleFormatsDateAdapter())
                .build()
    }

    @Singleton
    @Provides
    fun providePasswordDao(db: AppDatabase): PasswordDao {
        return db.passwordDao()
    }    
}

NOTE: Refer to this Dagger2 tutorial for a more complete sample.

NOTE: For non-singleton, you can look into Reusable scope.

Pass startup object/parameter via Binding Instances

You can use Binding Instances to pass object/paremeter during program startup (e.g. android application instance or command line arguments).

@Singleton
@Component(modules = [AndroidInjectionModule::class, AppModule::class, ActivityModule::class])
interface AppComponent {
    @Component.Builder
    interface Builder {
        @BindsInstance
        fun application(application: Application): Builder

        fun build(): AppComponent
    }
}
class LuaApp : Application() {
    override fun onCreate() {
        super.onCreate()
        DaggerAppComponent.builder().application(this).build()
    }
}

Runtime injection

Create a module to provide runtime value using singleton object as storage.

// a module to provide value via singleton object
@Module
internal class RuntimeModule {

    @Provides
    fun provideUsername() = RuntimeInjection.username
}
// singleton object as storage
object RuntimeInjection {
    var username: String = ""
}
// Assign value to singleton object during runtime
RuntimeInjection.username = "Desmond"

NOTE: Make sure to include Module via @Module(includes = [RuntimeModule::class]) in AppModule or @Component(modules = [..., [RuntimeModule::class]) in AppComponent.

NOTE: If you are providing multiple values of the same type object (e.g. username and password, which are both string), use Qualifiers.

NOTE: I am not sure if this is a good practice/pattern.

Assisted injection with AutoFactory

For assisted injection (with runtime paramaters), you might want to look into AutoFactory.

Inject / Retrieve Object

Access objects via constructor @Inject, fields @Inject or access value from dagger component.

Constructor @Inject

class PasswordViewModel @Inject constructor(private val dataSource: PasswordDao, private val categoryDataSource: CategoryDao) : ViewModel() {

}

NOTE: Refer to Inject ViewModel into Activity.

class Foo @Inject constructor (private val bar: Bar) {
  
}

NOTE: Refer to Injection for basic classes (for non-Activity/Fragment class).

Fields @Inject

Sometimes we are not responsible for object instantation (e.g. Activity, Fragment, WorkManager, etc), thus fields injection is required.

class MainActivity : AppCompatActivity() {
    @Inject
    lateinit var moshi: Moshi

    @Inject
    lateinit var sharedPref: SharedPreferences

    @Inject
    lateinit var viewModelFactory: ViewModelFactory
}

NOTE: Refer to Activity Injection.

NOTE: Refer to Fragment Injection.

NOTE: Refer to Injection for basic classes (for non-Activity/Fragment class).

Component Getter

@Singleton
@Component(...)
interface AppComponent {
    ...

    fun getMoshi(): Moshi
}
val component = DaggerAppComponent.builder().build()
val moshi = component.getMoshi()

NOTE: Refer to Alternative To Dagger Static Injection Using Component Getter for a full sample.

References:

This work is licensed under a
Creative Commons Attribution-NonCommercial 4.0 International License.