background
Why use itDependency injection
?
Nowadays, most applications have passed in the dependent class object through dependency injection, so as to improve the code extensibility and flexibly replace the dependent class in the development process.
Dependency injection
What is?
Create dependent class objects externally and pass them to the class (demander) for use through interface parameters.
Dependency injection framework provided by GoogleHilt
In our usual projects, the creation and dependency injection of class objects involve dozens or hundreds of classes, which are very complicated. It’s easy to fall into thankless territory when you do these things yourself. So Google provides a dependency injection framework called Hilt. After configuring class objects to be created and dependency relationships between classes in hilT mode, HilT can automatically create objects, manage object life cycles, and dependency injection.
How to Get started quickly
Looking back on the process of learning DI, I began to get familiar with Hilt with the following questions
- How do I tell “Hilt” that the current variable needs to be instantiated
- How do I tell “Hilt” where to get an instance
- How do I tell “Hilt” whether the resulting instance should be provided as a singleton
From a usage perspective, “Hilt” identifies and solves these problems by parsing annotations. So, using “Hilt” is largely a matter of learning from the annotations it provides.
What are the annotations provided by Hilt, and how do we choose them for our own needs?
Using the step
@HiltAndroidApp
: Initialization: Custom Application is added for it
```
@HiltAndroidApp
class MainApplication : Application()
```
Copy the code
HiltAndroidAp, @HiltViewModel, AndroidEntryPoint
: Android Class
The instance needs to be injected
- Application —> @HiltAndroidApp
- ViewModel —> @HiltViewModel
- Activity —> @AndroidEntryPoint
- Fragment —> @AndroidEntryPoint
AndroidEntryPoint class MainActivity: AppCompatActivity() {@inject lateinit var analytics: AnalyticsAdapter} note
- If the
Android Class
@ EntryPoint, statement. - The variable identified by @inject cannot be used
private
. - If you use
@AndroidEntryPoint
, its dependent class also needs to add this annotation, for exampleFragment
, then theHostActivity
Also need to be.
@Inject
: The current variable needs to be injected
“Hilt” looks for its class declaration, and its constructor, if it is @inject, returns by calling the constructor. Such as:
class AnalyticsAdapter @Inject constructor(
private val service: AnalyticsService
) { }
Copy the code
Those of you with some development experience will say, well, there’s no way to add @inject to constructors in a tripartite library like Retrofit. What if? —> @Module
@Module
,@Provides
: The current class passes@Provides
Annotation methods provide examples
@Module
@InstallIn(ActivityComponent::class)
object AnalyticsModule {
@Provides
fun provideAnalyticsService(
// Potential dependencies of this type
): AnalyticsService {
return Retrofit.Builder()
.baseUrl("https://example.com")
.build()
.create(AnalyticsService::class.java)
}
}
Copy the code
@Qualifier
If a Module has instances that can provide multiple interfaces, use different @qualifier to access them.
For example, the following example shows that creating okHttpClient requires an instance of @InterceptorBasic. For example, creating an OkHttpClient requires an instance of the @InterceptorBasic Interceptor.
import dagger.Module import dagger.Provides import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent import okhttp3.Interceptor import okhttp3.OkHttpClient import okhttp3.logging.HttpLoggingInterceptor import javax.inject.Qualifier @Qualifier @Retention(AnnotationRetention.BINARY) annotation class InterceptorBASIC @Qualifier @Retention(AnnotationRetention.BINARY) annotation class InterceptorHEADERS @Module @InstallIn(SingletonComponent::class) object NetworkModule { @Provides fun provideLogInterceptorOkHttpClient( @InterceptorHEADERS loggingInterceptor: Interceptor ): OkHttpClient { return OkHttpClient.Builder() .addInterceptor(loggingInterceptor) .build() } @Provides @InterceptorHEADERS fun provideLogInterceptorBASIC(): Interceptor { return HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BASIC) } @Provides @InterceptorBASIC fun provideLogInterceptorHEADERS(): Interceptor { return HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.HEADERS) } }Copy the code
@Binds
: Provides interface instances
interface AnalyticsService {
fun analyticsMethods()
}
// Constructor-injected, because Hilt needs to know how to
// provide instances of AnalyticsServiceImpl, too.
class AnalyticsServiceImpl @Inject constructor(
...
) : AnalyticsService { ... }
@Module
@InstallIn(ActivityComponent::class)
abstract class AnalyticsModule {
@Binds
abstract fun bindAnalyticsService(
analyticsServiceImpl: AnalyticsServiceImpl
): AnalyticsService
}
Copy the code
The code indicates that AnalyticsModule currently provides a specific implementation of AnalyticsService through the bindAnalyticsService method located at the Cursor-sharing interface. The return value of the bindAnalyticsService method tells the instance that is available, and its parameter represents the instance that is provided.
supplement
Component hierarchy
AndroidEntryPoint generates a Component for each Android Class, which accepts dependencies and is transitive.
@applicationCoText, @ActivityContext: prefabricated Qualifers
In projects, we often use context, and we can get ApplicationCotext and ActivityContext with these two annotations
@InstallIn()
We already know that Component means the current class needs to be injected. How does Component get the instance? Using @installin on the @Module annotated classes tells Hilt which classes these instances provided by the Module can be injected into.
How do I choose scope
The module of singletonComponet can provide instances of all the components pointed to by the arrow. But viewComponet is the only one that can get an instance.
Some advice
- The Activity Context cannot be held in the ViewModel.
This is because the ViewModel will save the instance when switching between horizontal and vertical screens, resulting in a memory leak