Hilt is what?

Hilt is a dependency injection framework for Android that relies on Dagger2. Dagger2 is what? Dagger is a dependency injection framework that Square did before, but it uses a lot of reflection, so Google thought it was a good thing, changed it, used compile-time annotations to improve the performance a lot and it’s called Dagger2, most foreign apps use Dagger2, But this framework is rarely used in China.

What is dependency injection?

To put it simply, if the value you need to construct an object is given to you by someone else, it is called dependency injection. If you create it yourself, it is not called dependency injection

class A1 {
    public A1(String name) {
        this.name = name;
    }

    private String name;

}

class A2 {
    public A2() {
        this.name = "wuyue";
    }

    private String name;

}
Copy the code

For example, if the name of the A1 class constructor is passed in, the process of building the A1 object is called dependency injection, because your A1 object depends on the value passed in

A2 A2’s constructor is just going to new itself. Then it’s not called dependency injection.

So DI is something that most people use every day.

What’s the use of these dependency injection frameworks when you’re using them every day?

That’s a good question. Dependency injection technology is used all the time. Why are we using dependency injection frameworks like Dagger2 and Hilt? The reason is that using these so-called dependency injection frameworks allows you to write a lot less code and is much easier to maintain.

If you create an object manually, then if the constructor of the object changes later, then you have to modify all the new places. Wouldn’t that be troublesome? If you have a dependency injection framework to help you with that then you really only have to change one place.

First simple example

In this example, let’s get familiar with the basic usage of Hilt.

Add dependencies in root project

Classpath 'com. Google. Dagger hilt - android - gradle - plugin: 2.28 alpha'Copy the code

And then in your app project

apply plugin: 'kotlin-kapt' apply plugin: . 'the dagger. The hilt. The android plugin' implementation "com. Google. Dagger: hilt - android: 2.28 - alpha" kapt Com. Google. "dagger hilt - android - the compiler: 2.28 alpha." "Copy the code

Note that custom Application should be annotated.

@HiltAndroidApp
class MyApplication:Application() {

}
Copy the code

First, define a class

data class Person constructor(val name: String, val age: Int) {
    @Inject
    constructor() : this("vivo", 18)
}
Copy the code

Note that this class uses the Inject annotation which essentially tells Hilt how to provide this object, okay

Then write our activity page

class MainActivity : AppCompatActivity() {

    @Inject
    lateinit var person: Person

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        Log.v("wuyue", "person:$person")
    }
}
Copy the code

Note that the @AndroidEntryPoint annotation is added. Similarly, you should use Inject annotation when declaring the Person object.

This is the simplest example of Hilt

The advantage is obvious, for example, in the future, the use of Person can not be written so, direct Inject can be I do not care about how the Person object is constructed, after the constructor changes, there is no need to modify the code.

Of course, there are people here who say, well, I understand the advantages of that, but no one actually uses that in Android programming,

Is there a better example?

Get the Retrofit/Okhttp object

Usually, when we’re working on a project, we’ll always have network requests, and these network requests will have some basic Retrofit or Okhttp object, and a lot of times we’ll write them as singletons and get them out. Is there an easier way to do that? some

// Retrofit service interface baiduiservice {} @module @InstallIn(ActivityComponent::class) object baidumodule {// Retrofit service interface BaiduiserVice {} @module @InstallIn(ActivityComponent::class) object Baidumodule { @Provides fun provideBaiduService():BaiduApiService{ return Retrofit.Builder().baseUrl("https://baidu.com").build().create(BaiduApiService::class.java) } }Copy the code

And then:

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {

    @Inject
    lateinit var baiduApiService: BaiduApiService

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

    }
}

Copy the code

Can. See if this dependency injection framework is much cleaner and easier to write than your previous singleton.

Here are a few notes to explain what they mean

@module is used in most scenarios to provide dependencies on constructors that cannot use @inject.

In the first example, we write the Person class ourselves. We add Inject annotations in front of the constructor

But what about third-party libraries like Retrofit where we can’t get their code and want to use Hilt

This is the Module, and when you use a Module, you usually use InstallIn annotations, followed by parameters that specify the scope of the Module

So let’s see how many ranges there are

And then finally, at sign Provides this annotation, which is pretty easy

This is usually used with @Module. You can add this annotation to any function that provides dependency injection.

How to deal with different details of multiple objects

So for example you can have multiple okHTTP clients in a project, right, and some of the interfaces we’re going to have to make an interceptor like print some buried points, some of the interfaces we’re going to have to make an interceptor to determine if the login is invalid, different scenarios, We need a new and different okHTTP client. Is there an easier way to write it? Yes!

@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class DataReportsOkHttpClient

@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class CheckTokenOkHttpClient
Copy the code

Let’s mark it up with the Qualifier

@Module
@InstallIn(ActivityComponent::class)
object OkHttpModule {
    @DataReportsOkHttpClient
    @Provides
     fun provideDataReportInterceptorOkHttpClient(
        dataReportInterceptor: DataReportInterceptor
    ): OkHttpClient {
        return OkHttpClient.Builder().addInterceptor(dataReportInterceptor).build()
    }

    @CheckTokenOkHttpClient
    @Provides
     fun provideCheckTokenInterceptorOkHttpClient(
        checkTokenInterceptor: CheckTokenInterceptor
    ): OkHttpClient {
        return OkHttpClient.Builder().addInterceptor(checkTokenInterceptor).build()
    }
}
Copy the code

And then the provides method here notice we’re going to add the Qualifier flag that we used earlier, at sign DataReportsOkHttpClient

But that’s not the end of the story. Here’s a principle:

The dependency injection component that uses Hilt must also have its own dependencies supplied by Hilt.

As you can see, both of our provide methods need a parameter. What does this parameter do? The function parameter is an OKHTTP interceptor.

But since this is a dependency injection module, the parameters you use must also be provided by dependency injection,

So here’s how you write the interceptor:


class DataReportInterceptor  : Interceptor {
  
    override fun intercept(chain: Interceptor.Chain): Response {
        return chain.proceed(chain.request())
    }

}
Copy the code

That is uncompilable because the Hilt component does not know how and where your object should be constructed, so here you must also specify that the interceptor’s construct is hilT-injected.

So all you have to do is:

class DataReportInterceptor  @Inject constructor() : Interceptor {
    init {
    }

    override fun intercept(chain: Interceptor.Chain): Response {
        return chain.proceed(chain.request())
    }

}

class CheckTokenInterceptor @Inject constructor()  : Interceptor {

    init {
    }

    override fun intercept(chain: Interceptor.Chain): Response {
        return chain.proceed(chain.request())
    }

}


Copy the code

So Hilt knows where to go to get the dependency. (This place was not mentioned in the official document, which caused many people to write demo according to the official document.)

Some tips

Hilt implements this kind of Context by default. You don’t need to bother constructing a Context. (You can’t actually construct a Context because it can’t be new.)

class AnalyticsAdapter @Inject constructor(
    @ActivityContext private val context: Context,
    private val service: AnalyticsService
) { ... }
Copy the code

And then, of course, the Application Context

We can also limit the scope of these dependency injection objects

If you are interested, you can go to the official website. That’s easy. I’m not going to do it.

If your object is an Activity, the fragment and view must be able to retrieve and share its state

It’s easy to understand if you can understand the Activity Fragment View.

Why the Hilt on earth

After we learned the previous basic example, we must think clearly about this problem, otherwise you can’t really understand this framework, after understanding him to use well.

Hilt solved the following problem:

What are we doing with so many scenarios in Android development? We build objects in the Activity, and how do we build those objects?

Most people are New, right, but the Class that these New objects belong to if the constructor changes,

We also have to find all references to this Class 11 to modify the calling method. This is really inconvenient.

Recalling our previous example, using Hilt can greatly avoid this scenario.