Hilt —一个新的Android依赖注入库

  • Post author:
  • Post category:其他


什么是依赖注入?

(

What’s Dependency Injection?

)


Dependency injection (DI)

is a unique way for classes to acquire references of other classes. For example, say that class

BananaMilkshake

requires the

Milk

class. Here,

BananaMilkShake

is dependent on the

Milk

class. Often these required classes, like

Milk

, are called dependencies.


依赖注入(DI)

是类获取其他类的引用的一种独特方式。 例如,假设类

BananaMilkshake

需要

Milk

类。 在这里,

BananaMilkShake

依赖于

Milk

类。 通常将这些必需的类(如

Milk

)称为依赖项。

Implementing DI provides you with the following advantages:

实施DI为您提供以下优点:

  • Reusability of code

    代码的可重用性

  • Ease of refactoring

    易于重构

  • Ease of testing

    易于测试

There are three types of dependency injections.

有三种类型的依赖项注入。

  1. Creating the required objects in the class itself (like with the

    Milk

    class object being created in the

    BananaMilkshake

    class)

    在类本身中创建所需的对象(例如在

    BananaMilkshake

    类中创建的

    Milk

    类对象)

  2. Taking the needed objects from somewhere else (like the context in the Android-activity component)

    从其他地方获取所需的对象(例如Android-activity组件中的上下文)

  3. Providing the required class object as a parameter (creating this may be through a constructor)

    提供所需的类对象作为参数(可以通过构造函数创建)

为您的应用选择正确的技术

(

Choosing the Right Technique for Your App

)

As the Android team recommends, if your app contains three or fewer screens, you can go without DI. But with more than three screens, it’s always recommended to use DI.

正如Android小组建议的那样,如果您的应用程序包含三个或更少的屏幕,则可以不用DI。 但是,如果有三个以上的屏幕,则始终建议使用DI。

It’ll be easier to choose DI if you have in-depth knowledge of what it’s providing.

如果您对DI所提供的内容有深入了解,那么选择DI会更容易。


  • Reusability:

    As the projects expand, it’ll be easier to create multiple implementations of the dependencies, but with DI inversion control, the dependent classes no longer control how the dependencies are created


    可重用性:

    随着项目的扩展,创建依赖关系的多个实现会更加容易,但是通过DI反转控制,依赖关系类将不再控制如何创建依赖关系


  • Ease of refactoring:

    The dependencies’ object creation can be checked at compile time rather than hidden at runtime


    易于重构:

    可以在编译时检查依赖项的对象创建,而不必在运行时隐藏


  • Ease of testing:

    With DI, it won’t be one class that creates dependencies, so it’ll be easier to test


    易于测试:

    使用DI不会是一个创建依赖关系的类,因此测试起来会更容易

Android中DI的历史

(

History of DI in Android

)

Long back in the early days of Android development, we didn’t even have the concept of DI. Developers created the objects whenever required, which led to the overuse of resources and no code reusability.

早在Android开发初期,我们甚至就没有DI的概念。 开发人员在需要时就创建了对象,这导致了资源的过度使用并且没有代码可重用性。

Slowly, developers started using

ServiceGenerator

s. The

ServiceGenerator

is a singleton class that’ll create the objects of the required class when necessary. If we request the class again, it’ll return the object that was created earlier.

慢慢地,开发人员开始使用

ServiceGenerator



ServiceGenerator

是一个单例类,将在必要时创建所需类的对象。 如果我们再次请求该类,它将返回之前创建的对象。

This solution solved the problem to some extent. But maintaining scopes for the objects is hectic in

ServiceGenerator

s, as it is for memory management. Then came the mighty

Dagger

library for DI in Android.

该解决方案在某种程度上解决了该问题。 但是在

ServiceGenerator

忙于维护对象的作用域,就像在内存管理中一样。 然后是强大的

Dagger

库,用于Android中的DI。

Dagger and Dagger2 had a great impact on implementing DI on Android. They brought many handy features to the development out of the box:

Dagger和Dagger2对在Android上实施DI产生了重大影响。 他们为开发带来了许多方便的功能:

  • Creating the

    AppContainer

    code (application graph) you manually implemented in the manual DI section

    创建您在手册DI部分中手动实现的

    AppContainer

    代码(应用程序图)

  • Building factories for the classes available in the application graph. This is how dependencies are satisfied internally and also why we don’t need to write all of the boilerplate code.

    为应用图中可用的类构建工厂。 这就是内部满足依赖关系的方式,也是为什么我们不需要编写所有样板代码的原因。

  • Reusing a dependency or creating new instances of a type depending on how you configure the type using scopes

    重用依赖项或创建类型的新实例,具体取决于您如何使用范围配置类型

  • Dagger also cares about memory management by releasing the objects that no longer have a use

    Dagger还通过释放不再使用的对象来关心内存管理。

The Dagger library solved almost every problem we face while implementing DI. But the learning curve is very deep, and we need to write so much boilerplate code despite Dagger code generation.

Dagger库几乎解决了我们在实现DI时面临的所有问题。 但是学习曲线非常深,尽管生成了Dagger代码,但我们仍然需要编写很多样板代码。

Recently, Yigit Boyar stated in a video that 49% of the requests came to the Android team are asking them to enhance the DI implementation process. That’s the main reason behind creating Hilt for Android.

最近,Yigit Boyar在视频中表示,有49%的请求来自Android团队,他们要求他们改善DI实施流程。 这是为Android创建Hilt的主要原因。

Hilt简介—本机DI解决方案

(

Introducing Hilt — A Native DI Solution

)

Hilt is built on top of Dagger to provide a standard way to implement DI in Android apps. So Hilt works much better than Dagger — and with a less complex implementation for developers.

Hilt建立在Dagger之上,提供了在Android应用中实现DI的标准方法。 因此,Hilt的工作原理比Dagger好得多,并且对开发人员而言,实现起来较为简单。

To implement DI using the Dagger library, we need to write modules, components, and more by ourselves. On top of that, every time we create a new Android component — like

Activity

,

Fragment

, or

Service

— we need to manually add them to the respective modules so they can be injected when necessary.

为了使用Dagger库实现DI,我们需要自己编写模块,组件等。 最重要的是,每次我们创建一个新的Android组件(例如

Activity



Fragment



Service

,我们都需要手动将它们添加到相应的模块中,以便在必要时可以将其注入。

Hilt, being developed specifically for Android, took measures that save a tos of time for developers. We no longer need to go through such an elaborate setup to inject

Fragment

s,

Activity

s, or

ViewModel

s.

专为Android开发的Hilt采取了一些措施,为开发人员节省了时间。 我们不再需要进行如此复杂的设置来注入

Fragment



Activity



ViewModel

With Hilt, we can use annotations such as:

使用Hilt,我们可以使用以下注释:


  • @HiltAndroidApp

    : We need to apply this annotation to our application class, which triggers the code generation and creates the base component


    @HiltAndroidApp

    :我们需要将此注释应用于我们的应用程序类,这将触发代码生成并创建基本组件


  • @AndroidEntryPoint

    : Hilt creates a dependency container to the Android component that it was assigned so it can inject the dependencies


    @AndroidEntryPoint

    :Hilt为分配给它的Android组件创建一个依赖容器,以便可以注入依赖

You’ll learn more about these annotations in detail in the following parts of this article.

您将在本文的以下部分中详细了解这些注释。

术语

(

Terminology

)

@HiltAndroidApp

(

@HiltAndroidApp

)

This annotation triggers Hilt’s code generation, including a base class for your application. The application container is the parent container of the app, which means that other containers (from Android components) can access the dependencies that it provides.

该注释触发Hilt的代码生成,包括您的应用程序的基类。 应用程序容器是应用程序的父容器,这意味着其他容器(来自Android组件)可以访问其提供的依赖项。

@AndroidEntryPoint

(

@AndroidEntryPoint

)

This annotation generates an individual Hilt component for each Android component so they can inject necessary dependencies.

此注释会为每个Android组件生成一个单独的Hilt组件,以便它们可以注入必要的依赖项。

@注入

(

@Inject

)

This is used in a constructor, field, or method where the dependency is requested.

在需要依赖项的构造函数,字段或方法中使用。

@模块

(

@Module

)

This is used on the classes that create the objects of dependency classes (for the classes that you don’t own — for example, third-party library classes like OkHttp or Retrofit).

它用于创建依赖关系类对象的类(对于您不拥有的类,例如,诸如OkHttp或Retrofit之类的第三方库类)。

@InstallIn

(

@InstallIn

)

The Hilt module class should also be annotated with @InstallIn to specify the scope of the module. For example, if we annotate the module with

@InstallIn(ActivityComponent::class)

, then the module will bind to the activity lifecycle.

Hilt模块类也应使用@InstallIn进行注释,以指定模块的范围。 例如,如果我们使用

@InstallIn(ActivityComponent::class)

注释模块,则该模块将绑定到活动生命周期。

@提供

(

@Provides

)

This is used on the methods that are inside the module class and provides the dependency objects.

它用于模块类内部的方法上,并提供依赖对象。

积分

(

Integration

)

Open the root

build.gradle

, and add the following Hilt dependency under the dependencies node. Have a look:

打开root

build.gradle

,并在依赖项节点下添加以下Hilt依赖项。 看一看:

buildscript {
    ...
    ext.hilt_version = '2.28-alpha'
    dependencies {
        ...
        classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version"
    }
}

We need to add the Hilt plugin for code generation purposes, as shown below:

我们需要添加Hilt插件以生成代码,如下所示:

...
apply plugin: 'dagger.hilt.android.plugin'


android {
    ...
}

Finally, we need to add the required dependencies to access the Hilt library in the project. Have a look:

最后,我们需要添加所需的依赖项以访问项目中的Hilt库。 看一看:

...
dependencies {
    ...
    implementation "com.google.dagger:hilt-android:$hilt_version"
    kapt "com.google.dagger:hilt-android-compiler:$hilt_version"
}

That’s it; we’ve successfully integrated the Hilt library.

而已; 我们已经成功集成了Hilt库。

提示设定

(

Hilt Setup

)

We don’t need a complicated setup to implement basic DI using Hilt. Primarily, we need to annotate our application class with

@HiltAndroidApp

so the application component will be created. Have a look:

我们不需要复杂的设置即可使用Hilt实现基本的DI。 首先,我们需要使用

@HiltAndroidApp

注释应用程序类,以便创建应用程序组件。 看一看:

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

So now we can inject classes in the application class, as shown below:

现在,我们可以在应用程序类中注入类,如下所示:

//Example class to be injected
class SampleClass @Inject constructor() {


    fun doYourWork() {
        Log.d("Hilt", "Do something")
    }
}




//Injecting the above class in the application after annotating with @HiltAndroidApp
@HiltAndroidApp
class ExampleApplication : Application() {


    @Inject
    lateinit var sampleClass: SampleClass


}

Now that we have application-level components, we can provide dependencies to other Android components that are annotated with

@AndroidEntryPoint

.

现在我们有了应用程序级别的组件,我们可以提供对其他使用

@AndroidEntryPoint

注释的Android组件的依赖关系。

First, let’s see how to implement

Activity

with this annotation:

首先,让我们看看如何使用此批注实现

Activity

@AndroidEntryPoint
class ExampleActivity : AppCompatActivity() { ... }

Hilt currently supports the following Android components:

Hilt当前支持以下Android组件:


  • Application

    (by using

    @HiltAndroidApp

    )


    Application

    (通过使用

    @HiltAndroidApp

    )


  • Activity


    Activity


  • Fragment


    Fragment


  • View


    View


  • Service


    Service


  • BroadcastReceiver


    BroadcastReceiver

Now can inject dependencies in those components that are annotated with

@AndroidEntryPoint

. Have a look:

现在可以将依赖项注入使用

@AndroidEntryPoint

注释的那些组件中。 看一看:

@AndroidEntryPoint
class ExampleActivity : AppCompatActivity() {


   @Inject lateinit var sampleClass: SampleClass
  ...
}

Here, we need to be aware of few things while working with Hilt and Android components:

在这里,我们在使用Hilt和Android组件时需要注意一些事项:

  • If you annotate an Android component such as a

    Fragment

    with

    @AndroidEntryPoint

    , then the other classes on which the annotated

    Fragment

    is dependent should also be annotated with

    @AndroidEntryPoint

    如果注释一个Android组分如

    Fragment



    @AndroidEntryPoint

    ,那么其他类在其上注释的

    Fragment

    是依赖也应与注释

    @AndroidEntryPoint

  • We don’t need to annotate with

    AndroidEntryPoint

    for the parent classes if they’re abstract

    如果父类是抽象类,我们不需要使用

    AndroidEntryPoint

    进行注释

  • When Hilt annotations are used on

    Activity

    , the injections take place in the

    onCreate

    function, so make sure to use those dependencies in a safe manner



    Activity

    上使用Hilt注释时,注入会在

    onCreate

    函数中进行,因此请确保以安全的方式使用这些依赖项

依靠第三方依赖性

(

Hilt With Third-Party Dependencies

)

To inject third-party dependencies like Retrofit into Android components like

Activity

s, we need to use modules. Modules are nothing but general classes annotated with

@Module

where we can create objects of third-party dependencies. Let’s see how to create a module class:

要将诸如Retrofit之类的第三方依赖项注入到

Activity

的Android组件中,我们需要使用模块。 模块不过是用

@Module

注释的通用类,我们可以在其中创建第三方依赖的对象。 让我们看看如何创建一个模块类:

@Module
@InstallIn(ApplicationComponent::class)
object NetworkModule {
  ...
}

The

@InstallIn

annotation provides a binding to the module with the Android component. Here, we’ve used

ApplicationComponent

, so

NetworkModule

binds to the application lifecycle.


@InstallIn

批注提供了与Android组件的模块绑定。 在这里,我们使用了

ApplicationComponent

,因此

NetworkModule

绑定到了应用程序生命周期。

Hilt provides a few more components you can use to bind dependencies to Android classes — have a look:

Hilt提供了一些其他组件,可用于将依赖项绑定到Android类-看一下:

Image for post

Screenshot from Android Developers official site
Android Developers官方网站的屏幕截图

Now we need to create functions with the

return

type as the desired dependencies. In this case, the dependencies are Retrofit and OkHttp. We also need to annotate those functions with

@Provides

so Hilt will be aware of them. Have a look:

现在,我们需要使用

return

类型作为所需的依赖关系来创建函数。 在这种情况下,依赖性为Retrofit和OkHttp。 我们还需要使用

@Provides

注释这些功能,以便Hilt会意识到它们。 看一看:

@Module
@InstallIn(ApplicationComponent::class)
object NetworkModule {


  
  @Provides
  @Singleton
  fun provideAuthInterceptorOkHttpClient(
    authInterceptor: AuthInterceptor
  ): OkHttpClient {
      return OkHttpClient.Builder()
               .addInterceptor(authInterceptor)
               .build()
  }
  
  @Provides
  @Singleton
  fun provideRetrofit(
    okHttpClient: OkHttpClient
  ): Retrofit {
      return Retrofit.Builder()
               .client(okHttpClient)
               .baseUrl("https://example.com")
               .build()
               .create(ApiServices::class.java)
  }


  
}

用ViewModels击中

(

Hilt With ViewModels

)

Until now, we’ve seen how to play around in Hilt with Android components and third-party libraries. Now, it’s time to take a step forward and work with

ViewModel

injection.

到目前为止,我们已经了解了如何在Hilt中使用Android组件和第三方库进行操作。 现在,该迈出一步并使用

ViewModel

注入了。

Before moving any further, we have to add a few additional libraries to our project to make Hilt work with

Jetpack

libraries like

ViewModel

s and

WorkManager

.

在继续进行之前,我们必须向项目中添加一些其他库,以使Hilt与

Jetpack

库一起使用,例如

ViewModel



WorkManager

Include the following libraries under the dependencies tag in the app-level

build.gradle

file.

在应用程序级别的

build.gradle

文件中的dependencies标记下包括以下库。

dependencies {
  ...
  implementation "androidx.fragment:fragment-ktx:1.2.4"
  implementation 'androidx.hilt:hilt-lifecycle-viewmodel:$hilt_jetpack_version'
  kapt 'androidx.hilt:hilt-compiler:$hilt_jetpack_version'
  kaptAndroidTest 'androidx.hilt:hilt-compiler:$hilt_jetpack_version'
}

These libraries provide extensions for Jetpack libraries like

ViewModel

and

WorkManger

to work with Hilt. They’ll generate the boilerplate code to save tons of time for developers.

这些库为Jetpack库(如

ViewModel



WorkManger

扩展提供了与Hilt一起使用的功能。 他们将生成样板代码以为开发人员节省大量时间。

Now let’s start with the

ViewModel

class. First, we need to include

@ViewModelInject

in the

ViewModel

constructor. This will tell Hilt how to provide the instance of the

ViewModel

. Have a look:

现在让我们从

ViewModel

类开始。 首先,我们需要在

ViewModel

构造函数中包含

@ViewModelInject

。 这将告诉Hilt如何提供

ViewModel

的实例。 看一看:

class MainViewModel @ViewModelInject constructor(
    private val Repository: Repository
): ViewModel() {
//...
}

Now, in Android components like

Activity

and

Fragment

, we can directly use them without

@inject

but with the

by viewModels()

delegate function. Have a look:

现在,在

Activity



Fragment

类的Android组件中,我们可以直接使用它们,而无需

@inject

而是使用

by viewModels()

委托函数。 看一看:

private val by viewModels()

If you want to use the

Activity

-level

ViewModel

, we need to apply the

by activityViewModels()

delegate function instead of

by viewModels()

. That’s all — you’re good to go with your MVVM architecture with Hilt.

如果要使用

Activity

级别的

ViewModel

,则需要应用

by activityViewModels()

委托函数而不是

by viewModels()

。 仅此而已–与Hilt一起使用MVVM架构非常好。


Important note:

Hilt is still it the Alpha stages, so you need Android Studio Canary version 4.0 to work with it. As it’s still in the Alpha stages, don’t use it on the production level yet.


重要说明:

击键仍然是Alpha阶段,因此您需要Android Studio Canary 4.0版才能使用它。 由于它仍处于Alpha阶段,因此请不要在生产级别上使用它。

奖金

(

Bonus

)

To learn more about DI, read the following articles.

要了解有关DI的更多信息,请阅读以下文章。

That’s all for now — hope you learned something useful. Thanks for reading.

现在就这些了-希望您学到了一些有用的东西。 谢谢阅读。

翻译自:

https://medium.com/better-programming/hilt-a-new-dependency-injection-library-for-android-e6e00e719aeb