Blog Infos
Author
Published
Topics
Published
Topics

In a typical Android application Dagger is initialized in app startup during the Application.onCreate.

A typical application may initialize Dagger and request a given dependency. When using Hilt the auto application code may look like the following:

@HiltAndroidApp
class MyApplication : Application() {
@Inject
lateinit var appInit: ApplicationInitializer
override fun onCreate() {
super.onCreate()
appInit.initTracking()
}
}

Initializing app using Dagger Hilt

 

The Hilt compiler plugin will generate code that is similar to manually initializing the component manually.

class MyApplication : Application() {
lateinit var componentManager: ApplicationComponentManager
@Inject
lateinit var appInit: ApplicationInitializer
override fun onCreate() {
super.onCreate()
val component = DaggerApplicationComponentManager
.applicationModule(ApplicationModule(this))
.build()
component.inject(myRepo)
appInit.initTracking()
}
}

Initializing app using vanilla Dagger

 

On Application startup we need to connect to a remote server and send some diagnostic data to a remote server and initialize tracking. As soon as initTrackingis called it will start a new thread and execute the work in the non-blocking lambda and return execution context to Application.onCreate.

class ApplicationInitializer @Inject constructor(
private val okHttpClient: OkHttpClient,
private val gson: Gson
) {
fun initTracking() {
thread {
// init async
}
}
}

ApplicationInitializer blocking Dagger inject

@InstallIn(SingletonComponent::class)
@Module
object NetworkModule {
@Singleton
fun provideOkHttp(): OkHttpClient {
return OkHttpClient.Builder().build()
}
@Singleton
fun provideGson(): Gson {
return GsonBuilder().create()
}
}

Dagger Module providing dependencies

 

When the app starts up it will execute Application.onCreate, create the component and inject the dependencies. In the Dagger module, providers marked with @Singleton will be lazily initialized once on the first usage. Certain dependencies such as OkHttp and Gson can take several hundred ms to initialize. In the above example the both provideOkHttp and provideGson will block in the Application.onCreate->component.inject() while the dependencies are initialized the first time. The usage of the dependencies is already non-blocking so ideally the initial creation would also be performed in a background thread.

blocking dagger startup

Fortunately Dagger provides a construct enabling deferred initialization called Provider. Provider is in interface with a single method get. The first time get() is called Dagger will create an instance of the dependency using the method annotated with @Provides. Subsequent calls will utilize a cached version. The advantage of using the Provider interface is that it enables additional control over when and where the dependency will be created.

package javax.inject;
public interface Provider<T> {
T get();
}

The above example could be refactored to the following:

class ApplicationInitializer @Inject constructor(
private val provideOkHttp: Provider<OkHttpClient>,
private val provideGson: Provider<Gson>
) {
fun initTracking() {
thread {
val okhttp = provideOkHttp.get()
val gson = provideGson.get()
// init async
}
}
}

Job Offers

Job Offers

There are currently no vacancies.

OUR VIDEO RECOMMENDATION

,

Demystifying Dagger

Dagger gets a bad rap for being “difficult to understand”, or “overly complicated”, or “black magic”.
Watch Video

Demystifying Dagger

Ryan Harter
Android
Dropbox

Demystifying Dagger

Ryan Harter
Android
Dropbox

Demystifying Dagger

Ryan Harter
Android
Dropbox

Jobs

 

Non-blocking startup

The above code will correctly initialize the dependencies using the Provider interface but there is nothing preventing another dependency from requesting the dependency in a blocking manner. To enforce this we can add an additional thread check.

@InstallIn(SingletonComponent::class)
@Module
object NetworkModule {
@Singleton
fun provideOkHttp(): OkHttpClient {
// verify not main thread
assert(Looper.getMainLooper() != Looper.myLooper())
return OkHttpClient.Builder().build()
}
@Singleton
fun provideGson(): Gson {
// verify not main thread
assert(Looper.getMainLooper() != Looper.myLooper())
return GsonBuilder().create()
}
}

It is common for dependencies to require other dependencies. For example a repository may require OkHttp and Gson to be initialized async. The module can be be updated as follows:

@InstallIn(SingletonComponent::class)
@Module
object NetworkModule {
@Singleton
fun provideOkHttp(): OkHttpClient {
// verify not main thread
assert(Looper.getMainLooper() != Looper.myLooper())
return OkHttpClient.Builder().build()
}
@Singleton
fun provideGson(): Gson {
// verify not main thread
assert(Looper.getMainLooper() != Looper.myLooper())
return GsonBuilder().create()
}
fun provideRepo(
provideOkHttp: Provider<OkHttpClient>,
provideGson: Provider<Gson>
): MainRepo {
return MainRepo(provideOkHttp::get, provideGson::get)
}
}

The repository can be updated to receive lambdas to enable async injection without adding Dagger dependencies.

class MainRepo(
val provideOkHttp: () -> OkHttpClient,
val provideGson: () -> Gson
) {
fun fetchSomething() {
thread {
val okHttp = provideOkHttp()
val gson = provideGson()
}
}
}

That is it. Please clap and follow me if you found this useful.

YOU MAY BE INTERESTED IN

YOU MAY BE INTERESTED IN

blog
Highlighting the advantages of DI is not the purpose of this article, but almost…
READ MORE
blog
In this blog post, I’m going to show you how to create custom-scoped components…
READ MORE
blog
This post will not only focus on manual & auto validation of TextField inputs,…
READ MORE
blog
We’ve been taking advantage of Kotlin Multiplatform at Klima for a while, but it’s…
READ MORE

Leave a Reply

Your email address will not be published. Required fields are marked *

Fill out this field
Fill out this field
Please enter a valid email address.

Menu