reference

Developer. The android. Google. Cn/training/DE…

In particular, it is important to note that studio4.2.1 cannot run the hilt version of the official website documentation

I use the dependent version configuration

Root Gradle dependency

ext.kotlin_version = "1.5.0"/ / kotlin version
ext.hilt_version = '2.35'/ / the hilt version
repositories {
    google()
    mavenCentral()
}
dependencies {
    classpath "Com. Android. Tools. Build: gradle: 2"
    classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
    classpath 'com. Google. Dagger hilt - android - gradle - plugin: 2.35'
}
Copy the code

2. App Gradle dependency

implementation "com.google.dagger:hilt-android:$hilt_version"
kapt "com.google.dagger:hilt-android-compiler:$hilt_version"
Copy the code

3. Plug-ins used

id 'kotlin-kapt'
id 'kotlin-android-extensions'
Copy the code

The code address

Github.com/ymeddmn/Hil…

Official branch: The official website demo

Onlysampletest: Easiest hilt to use

Hilt related comments

HiltAndroidApp: All applications that use Hilt need to use this annotation, which is used on the Application class

AndroidEntryPoint: Hilt can provide dependencies for other Android classes annotated with @AndroidEntryPoint. @AndroidEntryPoint can be used for the 4 components as well as views

@inject: Obtain dependencies

@hiltAndroidApp Example

@HiltAndroidApp
class App: Application(a){

    override fun onCreate(a) {
        super.onCreate()
    }
}
Copy the code

@AndroidEntryPoint and @Inject examples

@AndroidEntryPoint
public class ExampleActivity extends AppCompatActivity {

  @InjectAnalyticsAdapter analytics; . }Copy the code

@Binds

Module provides object generation methods externally. The methods decorated must be abstract

@Provides

Just like the @Binds, the method needs to have an implementation that provides an object generation method

Hilt basic use

Options must be configured

Add annotations to Application

@HiltAndroidApp
class App: Application() {}Copy the code

Onlysampletest branch code detail

This branch implements the function of injecting a Car object into MainActivity

1. First add the options that must be configured

2. Car object code:

class Car @Inject constructor() {
    fun drive(name: String) {
        println("The old driver$nameDrive online")}}//Car is constructed using @inject to indicate that the Car object is an injectable object
Copy the code

3. MainActivity code:

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {

    @Inject
    lateinit var car: Car//Car objects are annotated with @inject and will be instantiated automatically. In this case, lateinit must be used to delay initialization
    override fun onCreate(savedInstanceState: Bundle?). {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        car.drive("mage")}}Copy the code

Log output after code run:

The 2021-05-27 21:02:49. 874, 14104-14104 / com. Mage. Hiltdaggerdemo I/System. Out: mage online old drivers drive

Complextest branch code detail (more complex use)

scenario

In the OnLysampleTest branch we simply inject the Car into the MainActivity, but in the actual business scenario we would be faced with a much more responsible business. For example, we now want to assign a driver to the Car. Normally we would create a Driver Driver object and pass the Driver Driver object into the constructor of the Car object.

But that’s clearly not what Hilt wants

Hilt’s scenario implementation

Here’s how hilt is implemented:

First we need to create multiple classes

App Car Driver DriverImpl DriverModule MainActivity

The implementation of the Driver is passed into the construction of the Car,

A Driver is an abstract implementation of a Driver. It is an interface.

DriverImple is a Driver implementation class that can be any specific Driver.

DriverModule is responsible for providing the Driver class injection rules

Here’s the code:

1. Add options that must be configured

2. Car implementation

class Car @Inject constructor(val driver: Driver) {// Added an implementation of the Driver object to the Car constructor
    fun drive(a) {
        println("The old driver${driver.name}Drive online")}}Copy the code

2. Driver Interface

interface Driver {
    val name :String
}
Copy the code

3, DriverImple driver interface implementation

class DriverImpl @Inject constructor() : Driver {// Since DriverImpl needs to be injected into the Car construct, DriverImpl itself is injected, and its construct also needs to use @inject annotation
    override val name: String
        get() = "mage"
}
Copy the code

4. DriverModule Configures Driver injection rules

@Module// An annotation that must be configured to indicate that the object is a configuration rule for the Module
@InstallIn(ActivityComponent::class)// indicates that the configuration in this module is to be injected into the Activity
abstract class DriverModule {
    @Binds
    abstract fun bindDriver(driver: DriverImpl): Driver// The DriverImple parameter represents the actual Driver implementation to be injected into the Car constructor, and the return value Driver represents the abstract interface implemented by DriverImple
}
Copy the code

5. MainActivity code

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {

    @Inject
    lateinit var car: Car// For this to work, you must use lateinit to delay initialization
    override fun onCreate(savedInstanceState: Bundle?). {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        car.drive()
    }
}
Copy the code

6. Output logs

The 2021-05-27 21:30:43. 462, 18874-18874 / com. Mage. Hiltdaggerdemo I/System. Out: mage online old drivers drive

Provider branch code

The Provider branch is the interpretation of the @Provides annotation

In ComplexTest we share our custom objects using the @Binds annotation. If we share objects that are not shared by ourselves (such as OkhttpClient and Request), we can use @Provides. The following is a code example:

1. Add options that must be configured

Create OkhttpModule

@Module
@InstallIn(ActivityComponent::class)
object OkhttpModule {
    @Provides
    fun provideOkhttpClient(
        // Potential dependencies of this type
    ): OkHttpClient {
        return OkHttpClient()
    }

    @Provides
    fun providdeRequest(a):Request{
        return Request.Builder()
            .get()
            .url("https://developer.android.google.cn/training/dependency-injection/hilt-android")
            .build()
    }
}
Copy the code

3. MainActivity code

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {

    @Inject
    lateinit var client: OkHttpClient
    @Inject
    lateinit var request: Request
    override fun onCreate(savedInstanceState: Bundle?). {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        btn.setOnClickListener {
            client.newCall(request).enqueue(object :Callback{
                override fun onFailure(call: Call, e: IOException) {
                    println("Request failed")}override fun onResponse(call: Call, response: Response) {
                    println("Request successful")}})}}Copy the code

4. Output logs

The 2021-05-28 07:36:25. 477, 14692-14775 / com. Mage. Hiltdaggerdemo I/System. Out: request is successful

Hilt advanced use

Multipleobjtest branch use (providing multiple implementations of the same type)

In some cases, we may provide multiple instances of the same type of object, which requires special treatment. For example:

For example, we now provide an interface abstraction for Car, and then provide two interfaces for Car to implement TruckCar and TaxtCar simultaneously. In MainActivity the code looks like this:

@Inject
lateinit var truck: Car

@Inject
lateinit var taxi: Car
Copy the code

Since the injected objects are of Car type, it is impossible to tell whether we want TruckCar or TaxtCar, so the code will report errors during compilation. To solve this problem, you need to use custom annotations, as shown below:

1. Options must be configured

2, Car, TruckCar, TaxiCar code

interface Car {
    fun drive(name:String)
}
class TruckCar @Inject  constructor():Car{
    override fun drive(name: String) {
        println("$nameThe old truck driver drives, whooo.")}}class TaxiCar @Inject  constructor():Car{
    override fun drive(name: String) {
        println("$nameThe old taxi driver drives, whooo.")}}Copy the code

3. CarModule code

@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class Truck// Truck class injection tag

@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class Taxi// The taxi class is injected with the tag

@Module
@InstallIn(ActivityComponent::class)
class CarModule {

    @Truck// Add this annotation to the TruckCar class generation rule, and use this annotation to inject it so that the compiler knows that TruckCar is the type we want to inject
    @Provides
    fun bindTruckCar(truckCar: TruckCar): Car {
        return TruckCar()
    }

    @TaxiTaxiCar: TaxiCar: TaxiCar: TaxiCar: TaxiCar: TaxiCar: TaxiCar: TaxiCar: TaxiCar: TaxiCar: TaxiCar
    @Provides
    fun bindTaxtCar(taxiCar: TaxiCar): Car {
        return TaxiCar()
    }
}
Copy the code

4. Output logs

The 2021-05-28 08:12:28. 780, 20418-20418 / com. Mage. Hiltdaggerdemo I/System. Out: Mage old truck driver, whoops 08:12:28. 2021-05-28, 780, 20418-20418 / com. Mage. Hiltdaggerdemo I/System. Out: old mage taxi drivers drive, SOB

Predefined qualifier (@ApplicationContext @ActivityContext)

To copy the official explanation, Hilt provides some predefined qualifiers. For example, because you might need a Context class from an application or Activity, Hilt provides the @ApplicationContext and @ActivityContext qualifiers.

Example code:

1. Options must be configured

2, BaseInfo class

class BaseInfo @Inject constructor(@ActivityContext private val context: Context) {
    fun printPackageName(a){
        println("Application package name${context.packageName}")}}Copy the code

3, MainActivity class

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {

    @Inject
    lateinit var baseInfo: BaseInfo
    override fun onCreate(savedInstanceState: Bundle?). {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        btn.setOnClickListener {
            baseInfo.printPackageName()
        }
    }
}
Copy the code

4. Output logs

The 2021-05-28 08:20:56. 292, 21928-21928 / com. Mage. Hiltdaggerdemo I/System. Out: com application package name. The mage. Hiltdaggerdemo

Official class generation component

For each Android class from which you can perform field injection, there is an associated Hilt component that you can refer to in the @Installin annotation. Each Hilt component is responsible for injecting its bindings into the appropriate Android class.

For all components as follows, the lifecycle of the component and is dependent on the lifecycle of the injected component class

ApplicationComponent Application
ActivityRetainedComponent ViewModel Apply to the ViewModel
ActivityComponent Activity
FragmentComponent Fragment
ViewComponent View
ViewWithFragmentComponent with@WithFragmentBindingsThe annotationsView
ServiceComponent Service

Component scope

By default, all bindings in Hilt are not scoped. This means that every time a binding is requested, Hilt creates a new instance of the required type.

However, Hilt also allows you to limit the scope of a binding to a specific component. Hilt creates a scoped binding only once for each instance of the component to which the binding is scoped, and all requests to that binding share the same instance.

For example, when the component scope is @singleton, the component is a global Singleton

For example, when a component is scoped to @activityScoped, the component is the same object instance in the same Activity

So the default component and scope

Android class Generated component scope
Application ApplicationComponent @Singleton
View Model ActivityRetainedComponent @ActivityRetainedScope
Activity ActivityComponent @ActivityScoped
Fragment FragmentComponent @FragmentScoped
View ViewComponent @ViewScoped
with@WithFragmentBindingsThe annotationsView ViewWithFragmentComponent @ViewScoped
Service ServiceComponent @ServiceScoped

Default binding for components

Each Hilt component comes with a set of default bindings that Hilt can inject into your own custom bindings as dependencies. Note that these bindings correspond to regular Activity and Fragment types, not to any particular subclass. This is because Hilt uses a single Activity component definition to inject all activities. Each Activity has a different instance of this component.

Android components The default binding
ApplicationComponent Application
ActivityRetainedComponent Application
ActivityComponent ApplicationActivity
FragmentComponent Application,ActivityFragment
ViewComponent Application,ActivityView
ViewWithFragmentComponent Application,Activity,FragmentView
ServiceComponent ApplicationService