android 依赖注入_Android中的依赖注入

android 依赖注入

什么是依赖注入? (What is Dependency Injection?)

Classes often require references to other classes. For example, a Car class might need a reference to an Engine class. These required classes are called dependencies, the Car class is dependent on having an instance of the Engine class to run.

类通常需要引用其他类。 例如,Car类可能需要引用Engine类。 这些必需的类称为依赖项,Car类依赖于要运行Engine类的实例。

There are three ways for a class to get an object it needs:

类通过三种方式获取所需的对象:

  • The class constructs the dependency it needs. In the example above, Car would create and initialize its own instance of Engine.

    该类构造所需的依赖项。 在上面的示例中,Car将创建并初始化其自己的Engine实例。
  • Grab it from somewhere else. Some Android APIs, such as Context getters and getSystemService(), work this way.

    从其他地方抓取它。 某些Android API(例如Context getters和getSystemService())以这种方式工作。
  • Have it supplied as a parameter. The app can provide these dependencies when the class is constructed or pass them into the functions that need each dependency. In the example above, the Car constructor would receive Engine as a parameter.

    将其作为参数提供。 应用程序可以在构造类时提供这些依赖关系,或将它们传递给需要每个依赖关系的函数。 在上面的示例中,Car构造函数将接收Engine作为参数。

The third option is dependency injection! With this approach, you take the dependencies of a class and provide them rather than having the class instance obtain them itself.

第三种选择是依赖注入! 使用这种方法,您可以获取类的依赖关系并提供它们,而不是让类实例自己获取它们。

在Android中通过三种主要方式进行依赖项注入: (There are three major ways to do dependency injection in Android:)

  • Constructor Injection. This is the way described above. You pass the dependencies of a class to its constructor.

    构造函数注入。 这就是上面描述的方式。 您将类的依赖项传递给其构造函数。
  • Field Injection (or Setter Injection). Certain Android framework classes such as activities and fragments are instantiated by the system, so constructor injection is not possible. With field injection, dependencies are instantiated after the class is created.

    场注入(或二传手注入)。 系统会实例化某些Android框架类(例如活动和片段),因此无法进行构造函数注入。 使用字段注入,在创建类后实例化依赖项。
  • Method Injection, you can use @Inject annotation with method also.

    方法注入,您也可以在方法中使用@Inject注释。

Note: — If a class contains all types of Injection i.e constructor, field and method injection, dagger will inject all types in the following sequence :

注意:—如果一个类包含所有类型的Injection,即构造函数,字段和方法注入,则dagger将按以下顺序注入所有类型:

  1. Constructor

    建设者

  2. Field

    领域

  3. Method

    方法

依赖注入为您的应用程序提供以下优势: (Dependency injection provides your app with the following advantages:)

  • Reusability of classes and decoupling of dependencies: It’s easier to swap out implementations of a dependency. Code reuse is improved because of inversion of control, and classes no longer control how their dependencies are created, but instead work with any configuration.

    类的可重用性和依赖关系的解耦:交换依赖关系的实现更加容易。 由于控制反转,因此代码重用得到了改善,并且类不再控制其依赖关系的创建方式,而是可以与任何配置一起使用。
  • Ease of refactoring: The dependencies become a verifiable part of the API surface, so they can be checked at the object-creation time or at compile time rather than being hidden as implementation details.

    易于重构:依赖关系成为API表面的可验证部分,因此可以在对象创建时或编译时对其进行检查,而不必将其隐藏为实现细节。
  • Ease of testing: A class doesn’t manage its dependencies, so when you’re testing it, you can pass in different implementations to test all of your different cases.

    易于测试:类不管理其依赖关系,因此在测试它时,您可以传入不同的实现来测试所有不同的情况。

什么是Dagger2? (What is Dagger2?)

Dagger is a popular dependency injection library for Java, Kotlin, and Android that is maintained by Google. Dagger facilitates using DI in your app by creating and managing the graph of dependencies for you. It provides fully static and compile-time dependencies addressing many of the development and performance issues of reflection-based solutions such as Guice.

Dagger是Google维护的Java,Kotlin和Android流行的依赖项注入库。 Dagger通过为您创建和管理依赖关系图来促进在应用程序中使用DI。 它提供了完全静态和编译时的依赖性,解决了基于反射的解决方案(例如Guice)的许多开发和性能问题。

To demonstrate how you can work with Dagger, let’s create a simple factory for the UserRepository class shown in the following diagram:

为了演示如何使用Dagger,让我们为下图所示的UserRepository类创建一个简单的工厂:

Image for post

Dagger components

匕首组件

Dagger can create a graph of the dependencies in your project that it can use to find out where it should get those dependencies when they are needed. To make Dagger do this, you need to create an interface and annotate it with @Component.

Dagger可以在您的项目中创建依赖关系图,可用于查找需要时从何处获取这些依赖关系。 为了使Dagger做到这一点,您需要创建一个接口并使用@Component对其进行注释。

Inside the @Component interface, you can define functions that return instances of the classes you need (i.e. UserRepository). @Component tells Dagger to generate a container with all the dependencies required to satisfy the types it exposes. This is called a Dagger component; it contains a graph that consists of the objects that Dagger knows how to provide and their respective dependencies.

在@Component接口内,您可以定义函数以返回所需类的实例(即UserRepository)。 @Component告诉Dagger生成一个容器,该容器具有满足其公开的类型所需的所有依赖关系。 这称为Dagger组件; 它包含一个图形,该图形由Dagger知道如何提供的对象及其各自的依存关系组成。

// @Component makes Dagger create a graph of dependencies
    @Singleton
    @Component
    interface ApplicationGraph {
      // The return type of functions inside the component interface is
     // what can be provided from the container
          fun repository(): UserRepository
      }

Add an @Inject annotation to the UserRepository constructor so Dagger knows how to create a UserRepository.

将@Inject批注添加到UserRepository构造函数,以便Dagger知道如何创建UserRepository。

// @Inject lets Dagger know how to create instances of this object
class UserRepository @Inject constructor(
    private val localDataSource: UserLocalDataSource,
    private val remoteDataSource: UserRemoteDataSource
) { ... }

Now Dagger knows how to create an instance of UserRepository, but it doesn't know how to create its dependencies. If you annotate the other classes too, Dagger knows how to create them:

现在Dagger知道如何创建UserRepository的实例,但它不知道如何创建其依赖项。 如果您还要注释其他类,则Dagger知道如何创建它们:

// @Inject lets Dagger know how to create instances of these objects
class UserLocalDataSource @Inject constructor() { ... }
class UserRemoteDataSource @Inject constructor() { ... }

When you build the project, Dagger generates an implementation of the ApplicationGraph interface for you: DaggerApplicationGraph

在构建项目时,Dagger会为您生成ApplicationGraph接口的实现:DaggerApplicationGraph

// Create an instance of the application graph
val applicationGraph: ApplicationGraph = DaggerApplicationGraph.create()
// Grab an instance of UserRepository from the application graph
val userRepository: UserRepository = applicationGraph.repository()

Above are the basics of constructor injection using Dagger2.

上面是使用Dagger2进行构造函数注入的基础。

开始在Android中使用Dagger2(现场注入) (Start Working with Dagger2 in Android (Field Injection))

Adding dependencies

添加依赖

apply plugin: 'kotlin-kapt'


...


dependencies {
  implementation 'com.google.dagger:dagger:2.x'
  kapt 'com.google.dagger:dagger-compiler:2.x'
}

Consider an example Android app with the dependency graph : -

考虑带有依赖关系图的示例Android应用程序:-

Image for post

Because certain Android framework classes such as activities and fragments are instantiated by the system, Dagger can’t create them for you. For activities specifically, any initialization code needs to go into the onCreate() method. That means you cannot use the @Inject annotation in the constructor of the class (constructor injection) as you did in the previous examples. Instead, you have to use field injection.

由于某些Android框架类(例如活动和片段)是由系统实例化的,因此Dagger无法为您创建它们。 对于特定的活动,任何初始化代码都需要放入onCreate()方法中。 这意味着您不能像前面的示例中那样在类的构造函数(构造函数注入)中使用@Inject批注。 相反,您必须使用场注入。

class LoginActivity: Activity() {    
    // You want Dagger to provide an instance of LoginViewModel from the graph
    @Inject lateinit var loginViewModel: LoginViewModel
   }

Dagger needs to know that LoginActivity has to access the graph in order to provide the ViewModel it requires.

Dagger需要知道LoginActivity必须访问图形才能提供所需的ViewModel。

In this case, you need to tell Dagger about an object (LoginActivity in this case) that requires a dependency to be injected. For that, you expose a function that takes as a parameter the object that requests injection.

在这种情况下,您需要告诉Dagger有关需要注入依赖项的对象(在这种情况下为LoginActivity)。 为此,您公开了一个函数,该函数将请求注入的对象作为参数。

@Component
interface ApplicationComponent {
    // This tells Dagger that LoginActivity requests injection so the graph needs to
    // satisfy all the dependencies of the fields that LoginActivity is requesting.
    fun inject(activity: LoginActivity)
}

Note — When using activities, inject Dagger in the activity’s onCreate() method before calling super.onCreate() to avoid issues with fragment restoration. During the restore phase in super.onCreate(), an activity attaches fragments that might want to access activity bindings.

注—使用活动时,请在调用super.onCreate()之前将Dagger插入活动的onCreate()方法中,以避免片段恢复问题。 在super.onCreate()的还原阶段,活动会附加可能要访问活动绑定的片段。

When using fragments, inject Dagger in the fragment’s onAttach() method. In this case, it can be done before or after calling super.onAttach().

使用片段时,将Dagger插入片段的onAttach()方法中。 在这种情况下,可以在调用super.onAttach()之前或之后完成。

class LoginActivity: Activity() {
      // You want Dagger to provide an instance of LoginViewModel from the graph
      @Inject lateinit var loginViewModel: LoginViewModel
	override fun onCreate(savedInstanceState: Bundle?) {
	// Make Dagger instantiate @Inject fields in LoginActivity
	 (applicationContext as MyApplication).appComponent.inject(this)
	 // Now loginViewModel is available
	 super.onCreate(savedInstanceState)
   	 }
}
// @Inject tells Dagger how to create instances of LoginViewModel
class LoginViewModel @Inject constructor(
   private val userRepository: UserRepository
) { ... }

Let’s tell Dagger how to provide the rest of the dependencies to build the graph:

让我们告诉Dagger如何提供其余的依赖关系来构建图形:

class UserRepository @Inject constructor(
    private val localDataSource: UserLocalDataSource,
    private val remoteDataSource: UserRemoteDataSource
) { ... }
class UserLocalDataSource @Inject constructor() { ... }
class UserRemoteDataSource @Inject constructor(
    private val loginService: LoginRetrofitService
) { ... }

Note: — All injected constructor, fields and methods should not be private.

注意:—所有注入的构造函数,字段和方法都不应该是私有的。

Dagger Modules

匕首模块

Apart from the @Inject annotation, there’s another way to tell Dagger how to provide an instance of a class: the information inside Dagger modules. A Dagger module is a class that is annotated with @Module. There, you can define dependencies with the @Provides annotation.

除了@Inject批注之外,还有另一种告诉Dagger如何提供类实例的方法:Dagger模块内部的信息。 Dagger模块是用@Module注释的类。 在那里,您可以使用@Provides批注定义依赖关系。

// @Module informs Dagger that this class is a Dagger Module
@Module
class NetworkModule {
    // @Provides tell Dagger how to create instances of the type that this function
    // returns (i.e. LoginRetrofitService).
    // Function parameters are the dependencies of this type.
    @Provides
    fun provideLoginRetrofitService(): LoginRetrofitService {
	 // Whenever Dagger needs to provide an instance of type LoginRetrofitService,
	 // this code (the one inside the @Provides method) is run.
	 return Retrofit.Builder()
		 .baseUrl("https://example.com")
		 .build()
		 .create(LoginService::class.java)
    }
}

In order for the Dagger graph to know about this module, you have to add it to the @Component interface as follows:

为了使Dagger图了解此模块,必须将其添加到@Component接口,如下所示:

// The "modules" attribute in the @Component annotation tells Dagger what Modules
// to include when building the graph
@Component(modules = [NetworkModule::class])
interface ApplicationComponent {
    ...
}

Note: — You can also use @Binds annotation in place of @Provides but @Binds work with an abstract method.

注意:—您也可以使用@Binds批注代替@Provides,但是@Binds使用抽象方法。

Dagger subcomponents

匕首子组件

If your login flow (managed by a single LoginActivity) consists of multiple fragments, you should reuse the same instance of LoginViewModel in all fragments. @Singleton cannot annotate LoginViewModel to reuse the instance for the following reasons:

如果您的登录流(由单个LoginActivity管理)包含多个片段,则应在所有片段中重用LoginViewModel的相同实例。 由于以下原因,@ Singleton无法注释LoginViewModel重用实例:

The instance of LoginViewModel would persistent in memory after the flow has finished.

流完成后,LoginViewModel实例将在内存中保留。

You want a different instance of LoginViewModel for each login flow. For example, if the user logs out, you want a different instance of LoginViewModel, rather than the same instance as when the user logged in for the first time.

您需要每个登录流的LoginViewModel的不同实例。 例如,如果用户注销,则需要其他LoginViewModel实例,而不是与用户首次登录时相同的实例。

To scope LoginViewModel to the lifecycle of LoginActivity you need to create a new component (a new subgraph) for the login flow and a new scope.

要将LoginViewModel的范围限定为LoginActivity的生命周期,您需要为登录流程和新的范围创建一个新组件(一个新的子图)。

Let’s create a graph specific to the login flow.

让我们创建一个特定于登录流程的图形。

// @Subcomponent annotation informs Dagger this interface is a Dagger Subcomponent
@Subcomponent
interface LoginComponent {
    // This tells Dagger that LoginActivity requests injection from LoginComponent
    // so that this subcomponent graph needs to satisfy all the dependencies of the
    // fields that LoginActivity is injecting
    fun inject(loginActivity: LoginActivity)
}

LoginComponent must be able to access the objects from ApplicationComponent because LoginViewModel depends on UserRepository. The way to tell Dagger that you want a new component to use part of another component is with Dagger subcomponents. The new component must be a subcomponent of the component containing shared resources.

LoginComponent必须能够从ApplicationComponent访问对象,因为LoginViewModel取决于UserRepository。 告诉Dagger您希望新组件使用其他组件的一部分的方法是使用Dagger子组件。 新组件必须是包含共享资源的组件的子组件。

You also must define a subcomponent factory inside LoginComponent so that ApplicationComponent knows how to create instances of LoginComponent.

您还必须在LoginComponent内定义一个子组件工厂,以便ApplicationComponent知道如何创建LoginComponent的实例。

@Subcomponent
interface LoginComponent {
    // Factory that is used to create instances of this subcomponent
    @Subcomponent.Factory
    interface Factory {
	 fun create(): LoginComponent
    }
    fun inject(loginActivity: LoginActivity)
}

To tell Dagger that LoginComponent is a subcomponent of ApplicationComponent, you have to indicate it by:

要告诉Dagger LoginComponent是ApplicationComponent的子组件,必须通过以下方式指示它:

  • Creating a new Dagger module (e.g. SubcomponentsModule) passing the subcomponent’s class to the subcomponents attribute of the annotation.

    创建一个新的Dagger模块(例如SubcomponentsModule),将子组件的类传递给注释的subcomponents属性。
// The "subcomponents" attribute in the @Module annotation tells Dagger what
// Subcomponents are children of the Component this module is included in.
@Module(subcomponents = LoginComponent::class)
class SubcomponentsModule {}
  • Adding the new module (i.e. SubcomponentsModule) to ApplicationComponent:

    将新模块(即SubcomponentsModule)添加到ApplicationComponent中:
// Including SubcomponentsModule, tell ApplicationComponent that
// LoginComponent is its subcomponent.
@Singleton
@Component(modules = [NetworkModule::class, SubcomponentsModule::class])
interface ApplicationComponent {
}
  • Expose the factory that creates instances of LoginComponent in the interface:

    公开在界面中创建LoginComponent实例的工厂:
@Singleton
@Component(modules = [NetworkModule::class, SubcomponentsModule::class])
interface ApplicationComponent {
// This function exposes the LoginComponent Factory out of the graph so consumers
// can use it to obtain new instances of LoginComponent
fun loginComponent(): LoginComponent.Factory
}

You can use the ApplicationComponent to get a reference to LoginComponent and then inject LoginActivity as follows:

您可以使用ApplicationComponent获取对LoginComponent的引用,然后按如下所示注入LoginActivity:

class LoginActivity: Activity() {
	// Reference to the Login graph
    lateinit var loginComponent: LoginComponent
    // Fields that need to be injected by the login graph
    @Inject lateinit var loginViewModel: LoginViewModel
    override fun onCreate(savedInstanceState: Bundle?) {
	 // Creation of the login graph using the application graph
	 loginComponent = (applicationContext as MyDaggerApplication)
					appComponent.loginComponent().create()
	// Make Dagger instantiate @Inject fields in LoginActivity
	 loginComponent.inject(this)
	 // Now loginViewModel is available
	 super.onCreate(savedInstanceState)
    }
}

Now, if you had two fragments that need LoginViewModel, both of them are provided with the same instance. For example, if you have a LoginUsernameFragment and a LoginPasswordFragment they need to get injected by the LoginComponent:

现在,如果您有两个需要LoginViewModel的片段,则它们都具有相同的实例。 例如,如果您有一个LoginUsernameFragment和LoginPasswordFragment,则它们需要由LoginComponent注入:

@ActivityScope
@Subcomponent
interface LoginComponent {
    @Subcomponent.Factory
    interface Factory {
	fun create(): LoginComponent
    }
    // All LoginActivity, LoginUsernameFragment and LoginPasswordFragment
    // request injection from LoginComponent. The graph needs to satisfy
    // all the dependencies of the fields those classes are injecting
    fun inject(loginActivity: LoginActivity)
    fun inject(usernameFragment: LoginUsernameFragment)
}

The components access the instance of the component that lives in the LoginActivity object. Example code for LoginUserNameFragment appears in the following code snippet:

组件访问位于LoginActivity对象中的组件实例。 以下代码段中显示了LoginUserNameFragment的示例代码:

class LoginUsernameFragment: Fragment() {
	// Fields that need to be injected by the login graph
    @Inject lateinit var loginViewModel: LoginViewModel
    override fun onAttach(context: Context) {
	 super.onAttach(context)
	// Obtaining the login graph from LoginActivity and instantiate
	// the @Inject fields with objects from the graph
	 (activity as LoginActivity).loginComponent.inject(this)
	 // Now you can access loginViewModel here and onCreateView too
	 // (shared instance with the Activity and the other Fragment)
    }
}

These are the basics to start working with dagger in Android. There is a lot of things in dagger. The best way to learn Dagger is by playing with it.

这些是开始在Android中使用匕首的基础知识。 匕首里有很多东西。 学习Dagger的最好方法是通过使用它。

翻译自: https://medium.com/@nikitaverma081996/dependency-injection-in-android-6eca8d41ae17

android 依赖注入

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值