Android Analysis of Hilt Dependency Injection

What is Dependency Injection?

First of all, a member variable of a certain class is called dependency. If this variable wants to instantiate a method that refers to its class, it can pass parameters through the constructor or obtain the object through a certain method. Such methods of obtaining object instances through external methods are called Dependency injection; and dependency injection can be simply divided 手动注入into 自动注入two methods; it is a dependency injection library based on HiltDagger , Hilt is a dependency injection library specially created by Google for the Android platform, which is greatly simplified in use ( 场景化优化compared to dagger)

Benefits of using dependency injection

The advantages described in the Google docs are as follows:

  • reuse code
  • easy to refactor
  • easy to test

Simply put, some content needs to be manually called layer by layer during development, and it needs to be instantiated manually; for Hilt, it can automatically perform dependency injection, and provide a container for each Android class in the project and automatically manage it Its life cycle; some data that needs to be used globally can be shared through Hilt, thereby simplifying the code

Commonly used predefined qualifiers in Hilt

@HiltAndroidApp

All applications using Hilt must contain an @HiltAndroidAppannotated Application class. @HiltAndroidAppIt will trigger Hilt's code generation operation. The generated code includes a base class of the application, which acts as an application-level dependency container. Remember that this Application class must be referenced in the manifest file

@HiltAndroidApp
class App : Application() {
    
     ... }

@AndroidEntryPoint

If an Activity wants to use Hilt dependency injection, it must be added to the Activity @AndroidEntryPoint注解. Similarly, a Fragment in the Activity also needs to be used and must be added @AndroidEntryPoint注解;
currently Hilt supports the following Android classes:

  • Application (by using @HiltAndroidApp)

  • ViewModel (by using @HiltViewModel)

  • Activity

  • Fragment

  • View

  • Service

  • BroadcastReceiver

@AndroidEntryPoint
class MainActivity : ComponentActivity() {
    
    ...}

@Module

Hilt has a module class, using @Modulethe annotation class, it will tell Hilt which instances it provides, usually @Providerused in conjunction with it, you must @InstallInadd scope to it through annotations

@InstallIn

For each Android class from which you can perform field injection, there is an associated Hilt component that you can reference in @InstallInannotations . Each Hilt component is responsible for injecting its bindings into the corresponding Android class.

Android components default binding
SingletonComponent Application
ActivityRetainedComponent Application
ViewModelComponent SavedStateHandle
ActivityComponent Application 和 Activity
FragmentComponent Application、Activity 和 Fragment
ViewComponent Application、Activity 和 View
ViewWithFragmentComponent Application、Activity、Fragment 和 View
ServiceComponent Application and Service
@Module
@InstallIn(SingletonComponent::class)
object AppModule {
    
    ...}

@Provides

If you want to let Hilt know which instance objects are provided, you can use them @Providesto make annotations. A database instance is provided below, and the scope is a global singleton

    @Provides
    @Singleton
    fun providerAccountBean():AccountBean{
    
    
        return AccountBean("FranzLiszt","123456")
    }

@Inject

If you want to use Hilt to automatically implement dependency injection objects, you can use @Injectannotations to call them; as follows, without instantiation, it will automatically refer to the providerAccountBean()provided objects

 @Inject lateinit var bean: AccountBean

@HiltViewModel

It can be understood from the name that it needs to ViewModelbe used in conjunction with the class to act on the ViewModel; as follows, @Injectit is used in conjunction with the constructor to reference Hiltthe object provided above

@HiltViewModel
class AccountViewModel @Inject constructor(private val bean: AccountBean):ViewModel() {
    
    ...}

The use of Hilt

rely

Step 1: project/build.gradleAdd the following code in

plugins {
    
    
    ...
    id 'com.google.dagger.hilt.android' version '2.44' apply false
}

Step 2: app/build.gradleAdd the following code in

plugins {
    
    
    ...
    id 'kotlin-kapt'
    id 'dagger.hilt.android.plugin'
}

Step 3: app/build.gradleAdd the following code in

   //Dagger - Hilt
    implementation("com.google.dagger:hilt-android:2.44")
    kapt("com.google.dagger:hilt-android-compiler:2.44")

    // Compose dependencies
    implementation "androidx.lifecycle:lifecycle-viewmodel-compose:2.4.0-beta01"
    implementation "androidx.hilt:hilt-navigation-compose:1.0.0-alpha03"

Step 4: app/build.gradleAdd the following code in

android{
    
    
...
 compileOptions {
    
    
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}

create entity class

data class AccountBean(var username:String,var password:String)

Add Hilt entry

@HiltAndroidApp
class APP:Application() {
    
    }

provide object

As a use case here, just simply write a test case, you can write a complex program in the function, and it will not affect the performance of the program. It will take a little time when the program is initialized and compiled. Provide a singleton AccountBeanobject

@Module
@InstallIn(SingletonComponent::class)
object AppModule {
    
    

    @Provides
    @Singleton
    fun providerAccountBean():AccountBean{
    
    
        return AccountBean("FranzLiszt","123456")
    }
}

get object

AndroidEntryPointAdd the Hilt annotation to the Activity , and then you can get the object provided by Injectthe above method through the annotationproviderAccountBean

@AndroidEntryPoint
class MainActivity : ComponentActivity() {
    
    
    @Inject lateinit var bean: AccountBean
    ...
    }

Then you can directly refer to the object

  @Composable
    fun showAccountInfo() {
    
    
        Column(
            verticalArrangement = Arrangement.Center,
            horizontalAlignment = Alignment.CenterHorizontally
        ) {
    
    
            Text(text = bean.username)
            Spacer(modifier = Modifier.height(20.dp))
            Text(text = bean.password)
        }
    }

Application with ViewModel

Use annotations in ViewModel HiltViewModel, and then you can use Injectannotations to get Hilt to automatically inject dependent objects

@HiltViewModel
class AccountViewModel @Inject constructor(private val bean: AccountBean):ViewModel() {
    
    ...}

Define two member variables with state, which AccountStateis a data class with two member variables text and hint

private val _usernameState = mutableStateOf(AccountState(
        hint = "input username"
    ))
    val usernameState:State<AccountState> = _usernameState

    private val _passwordState = mutableStateOf(AccountState(
        hint = "input password"
    ))
    val passwordState:State<AccountState> = _passwordState

In the ViewModel initialization function, assign the value of the object provided by Hilt to the two state variables in the VM

init {
    
    
        _usernameState.value = usernameState.value.copy(text = bean.username)
        _passwordState.value = passwordState.value.copy(text = bean.password)
    }

Corresponding processing is performed through external events, and the value of the external input box is continuously modified. Here, it is synchronized to the state variable in the VM.

   fun onEvent(event: AccountEvent){
    
    
        when(event){
    
    
            is AccountEvent.ChangeUsername ->{
    
    
                _usernameState.value = usernameState.value.copy(text = event.value)
            }
            is AccountEvent.ChangePassword ->{
    
    
                _passwordState.value = passwordState.value.copy(text = event.value)
            }
        }
    }

use

Here, directly obtain the values ​​of the two member variables of the ViewModel, and then bind them to the two input boxes. By listening to the method of the input box onValueChange, when the value is continuously modified, then call the method in the VM onEvent, and then modify the state content in the VM , since the input box is bound to the state variable of the VM, and after the value of the state variable changes, the corresponding UI interface will重组

    @Composable
    fun showAccountInfo (viewModel: AccountViewModel = hiltViewModel()){
    
    
        val username = viewModel.usernameState.value
        val password = viewModel.passwordState.value
        Column(
            verticalArrangement = Arrangement.Center,
            horizontalAlignment = Alignment.CenterHorizontally
        ) {
    
    
            TextField(
                value = username.text,
                placeholder = {
    
     Text(text = username.hint)},
                onValueChange = {
    
    
                    viewModel.onEvent(AccountEvent.ChangeUsername(it))
                }
            )

            Spacer(modifier = Modifier.height(20.dp))

            TextField(
                value = password.text,
                placeholder = {
    
     Text(text = password.hint)},
                onValueChange = {
    
    
                    viewModel.onEvent(AccountEvent.ChangePassword(it))
                }
            )
        }
    }

Summarize

The above two test cases are just the tip of the iceberg of the convenience provided by Hilt; Hilt is very convenient to use, and it can improve the code structure and save unnecessary code; the dependency Koininjection library is also well received, although I have not used it. However, because of Google's push Hilt, I started first; in terms of performance, I read some other articles, and the difference is not bad, so I can choose according to my own needs.

Guess you like

Origin blog.csdn.net/News53231323/article/details/128554310