Dagger Hilt: Difference between revisions
(3 intermediate revisions by the same user not shown) | |||
Line 2: | Line 2: | ||
This page is to summarize new Dagger Hilt | This page is to summarize new Dagger Hilt | ||
=Configuration= | =Configuration= | ||
Dagger hilt relies on kotlin-kapt and dagger.hilt.android.plugin | Dagger hilt relies on kotlin-kapt and dagger.hilt.android.plugin. In the root build.gradle.kts | ||
<syntaxhighlight lang="groovy"> | <syntaxhighlight lang="groovy"> | ||
plugins { | plugins { | ||
alias(libs.plugins.android.application) apply false | |||
alias(libs.plugins.kotlin.android) apply false | |||
alias(libs.plugins.kotlin.compose) apply false | |||
alias(libs.plugins.android.library) apply false | |||
id("com.google.dagger.hilt.android") version "2.51.1" apply false | |||
} | } | ||
</syntaxhighlight> | |||
In the app, domain or data build.gradle.kts | |||
<syntaxhighlight lang="groovy"> | |||
plugins { | |||
id("kotlin-kapt") | |||
id("com.google.dagger.hilt.android") | |||
} | |||
... | |||
dependencies { | dependencies { | ||
Line 20: | Line 29: | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=Example Repository Implementation= | =Example Repository Implementation= | ||
Here is the Implemented Repository | Here is the Implemented Repository | ||
Line 143: | Line 153: | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
==Example with Constructor== | |||
Here is the bind which is the same as above | |||
<syntaxhighlight lang="kotlin"> | |||
@Module | |||
@InstallIn(SingletonComponent::class) | |||
abstract class RepositoryModule { | |||
@Binds | |||
abstract fun bindDataRepository( | |||
repository: DataRepository | |||
): Repository | |||
} | |||
</syntaxhighlight> | |||
Here is the network module to be injected into the Repository Implementation | |||
<syntaxhighlight lang="kotlin"> | |||
@Module | |||
@InstallIn(SingletonComponent::class) | |||
class NetworkModule { | |||
@Provides | |||
@Singleton | |||
internal fun provideNetworkChecker(context: Application): NetworkChecker = NetworkChecker(context) | |||
} | |||
</syntaxhighlight> | |||
Here is the implementation | |||
<syntaxhighlight lang="kotlin"> | |||
class DataRepository @Inject | |||
constructor( | |||
private val networkChecker: NetworkChecker | |||
): Repository { | |||
override val isConnected: Boolean | |||
get() = networkChecker.isConnected | |||
} | |||
</syntaxhighlight> | |||
=Services= | =Services= | ||
For services we do not have a constructor but we do still have field injections. So we can do | For services we do not have a constructor but we do still have field injections. So we can do | ||
Line 159: | Line 203: | ||
return null | return null | ||
} | } | ||
} | |||
</syntaxhighlight> | |||
=Lazy Injections= | |||
Usually Injection happen on creation but you can make them happen at first use by using Lazy. E.g. | |||
<syntaxhighlight lang="kotlin"> | |||
@HiltViewModel | |||
class MyViewModel | |||
@Inject constructor( | |||
private val repository: Lazy<MyRepository> | |||
): ViewModel() { | |||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> |
Latest revision as of 21:56, 13 March 2025
Introduction
This page is to summarize new Dagger Hilt
Configuration
Dagger hilt relies on kotlin-kapt and dagger.hilt.android.plugin. In the root build.gradle.kts
plugins {
alias(libs.plugins.android.application) apply false
alias(libs.plugins.kotlin.android) apply false
alias(libs.plugins.kotlin.compose) apply false
alias(libs.plugins.android.library) apply false
id("com.google.dagger.hilt.android") version "2.51.1" apply false
}
In the app, domain or data build.gradle.kts
plugins {
id("kotlin-kapt")
id("com.google.dagger.hilt.android")
}
...
dependencies {
implementation "com.google.dagger:hilt-android:2.40.5"
kapt "com.google.dagger:hilt-compiler:2.40.5"
implementation "androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha03"
kapt "androidx.hilt:hilt-compiler:1.0.0"
implementation ("androidx.hilt:hilt-navigation-compose:1.0.0")
}
Example Repository Implementation
Here is the Implemented Repository
class MyRepositoryImpl (
private val api:MyApi
private val appContext: Applicaiton
): MyRepository {
override suspend fun doNetworkCall() {
....
}
Example Module
This is an example of an App Module. The InstallIn type decide how long the dependency will live. Examples are
- ActivityComponent
- ViewModelComponment
- SingletonComponent
- ActivityRetainedComponent Not destroyed when screen rotated
- ServiceComponent
Note the @Provides is no different to Dagger 2, the @Singleton, not to be confused with the above, decides how many instances to create of this module. In this case we want one instance across the app. If we did not specify @Singleton it would create one on each @Inject request. Note this example also shows an example where the MyRepository depends on MyApi.
@Module
@InstallIn(SingletonComponent::class)
object AppModule {
@Provides
@Singleton
fun provideMyApi(): MyApi {
return Retrofit.Builder()
.baseUrl("https://www.bibble.co.nz")
.build()
.create(MyApi::class.java)
}
@Provides
@Singleton
fun provideMyRepository(api: MyApi, app: Application): MyRepository {
return MyRepositoryImpl(api, app)
}
}
Example ViewModel
Now we are free to inject this into a ViewModel
@HiltViewModel
class MyViewModel
@Inject constructor(
private val repository: MyRepository
private val appContext: Application
): ViewModel() {
}
Example Activity
@AndroidEntryPoint
class MainActivity: ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MyTheme {
val viewModel = hiltViewModel<MyViewModel>()
}
}
}
}
Example Application
This is a bit easier than the old days.You will need to provide the Class name in the Manifest as you did with Dagger 2 e.g. name=".MyApp"
@HiltAndroidApp
class Myapp: Application()
Duplicate Injecti ons
You can name injects where the return type is the same e.g.
@Provides
@Singleton
@Named("Hello1")
fun provideString1() = "Hello 1"
@Provides
@Singleton
@Named("Hello2")
fun provideString2() = "Hello 2"
Binds
This is the example using @Provides.
@Provides
@Singleton
fun provideMyRepository(api: MyApi, app: Application): MyRepository {
return MyRepositoryImpl(api, app)
}
But we can achieve the same thing with bind. Where we are injecting an interface. This generates less code and seems to be the preferred approach
@Module
@InstallIn(SingletonComponent::class)
abstract class RepositoryModule {
@Binds
@Singleton
abstract fun bindMyRepository(
myRepositoryImpl: MyRepositoryImpl
): MyRepository
}
We have to change the Repository Implementation to now inject the abstract class like so
class MyRepositoryImpl
@Inject constructor(
private val api:MyApi
private val appContext: Applicaiton
): MyRepository {
override suspend fun doNetworkCall() {
....
}
Example with Constructor
Here is the bind which is the same as above
@Module
@InstallIn(SingletonComponent::class)
abstract class RepositoryModule {
@Binds
abstract fun bindDataRepository(
repository: DataRepository
): Repository
}
Here is the network module to be injected into the Repository Implementation
@Module
@InstallIn(SingletonComponent::class)
class NetworkModule {
@Provides
@Singleton
internal fun provideNetworkChecker(context: Application): NetworkChecker = NetworkChecker(context)
}
Here is the implementation
class DataRepository @Inject
constructor(
private val networkChecker: NetworkChecker
): Repository {
override val isConnected: Boolean
get() = networkChecker.isConnected
}
Services
For services we do not have a constructor but we do still have field injections. So we can do
@AndroidEntryPoint
class MyService: Service() {
@Inject
lazyinit var repository: MyRepository
override onCreate() {
super.onCreate()
repository.doNetworkCall()
}
override fun onBind(p0: Intent?): IBinder? {
return null
}
}
Lazy Injections
Usually Injection happen on creation but you can make them happen at first use by using Lazy. E.g.
@HiltViewModel
class MyViewModel
@Inject constructor(
private val repository: Lazy<MyRepository>
): ViewModel() {
}