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 injectionWhat 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 ClassThe instance needs to be injected

  • Application —> @HiltAndroidApp
  • ViewModel —> @HiltViewModel
  • Activity —> @AndroidEntryPoint
  • Fragment —> @AndroidEntryPoint

AndroidEntryPoint class MainActivity: AppCompatActivity() {@inject lateinit var analytics: AnalyticsAdapter} note

  • If theAndroid Class@ EntryPoint, statement.
  • The variable identified by @inject cannot be usedprivate.
  • If you use @AndroidEntryPoint, its dependent class also needs to add this annotation, for exampleFragment, then theHostActivityAlso 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@ProvidesAnnotation 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

  1. 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

reference