Android依赖注入– Dagger 2

时间:2020-02-23 14:28:53  来源:igfitidea点击:

在本教程中,我们将在android应用程序中讨论并实现依赖注入(DI)。
对于过去(可能在Spring)使用过DI的那些人,可以跳过以下部分来介绍Android Dependency Injection。

什么是Android依赖注入?

您不是自己创建对象,而是让别人为您创建对象。

让我们分析一下以上语句的含义。

想象以下情况:我们有一个Activity,并且希望将一些数据保存在SharedPreferences中。
在没有DI的情况下执行此操作将导致我们实例化,保存,从SharedPreferences中检索数据,所有这些都在Activity的样板代码内。
下图或者多或者少地描绘了某种东西。

没有DI

上面的方法可能会导致一些严重的问题,尤其是当您的代码库增大时。

您将遇到单元测试问题,太多样板代码,难以修改的当前代码库(有太多实例需要关注)(共享实例,作用域实例)。
其中Android Dependency Injection Pattern可以大大增强您的代码。

如依赖项中所述和下图所示,不是让每次在我们的Activity中实例化SharedPreferences,而是让它从另一个类中注入。

因此,我们的活动取决于SharedPreferences。
因此,SharedPreferences充当我们Activity的依赖项。
SharedPreferences从外部注入到我们的Activity中,而不是实例化。

为什么要使用Android依赖注入?

如果您是的老板,您希望自己做所有事情还是委派任务?

您是否愿意聘请新手,而不是已经知道任务的专家?

在理想情况下,以上两个问题的答案应该是后一种选择。

这就是依赖注入如此重要的原因。
与其每次在我们的活动中实例化SharedPreferences,数据库和网络库,我们都希望在其他地方创建它们的实例,并在需要时将它们注入到我们的活动中。

这些依赖关系通常在我们的类中使用构造函数,我们将很快看到。

什么是Dagger2?

Dagger库由Square的开发人员在2012年创建。
Dagger1用于创建类的实例并通过Reflections注入依赖项。
Dagger 2在第一个版本的基础上进行了改进,并与Google的开发人员团队合作,引入了一个更快,更完善的版本,而没有使用Reflections。

Dagger 2是一个编译时android依赖项注入框架,并使用Java Specification Request(JSR)330并使用注释处理器。

以下是Dagger 2中使用的基本注释:

  • @Module:此类用于构造最终将作为依赖项提供的对象的类。

  • @Provides:用于Module类中将返回对象的方法上。

  • @Inject:用于构造函数,字段或者方法,表示已请求依赖项。

  • @Component:Module类未直接向请求它的类提供依赖项。
    为此,使用了一个Component接口,它充当@Module和@Inject之间的桥梁。

  • @Singleton:这表明将仅创建依赖项对象的单个实例。

不用担心上面的东西会打扰您。
现在,我们将开发一个使用Dagger并提供SharedPreferences作为我们Activity中的依赖项的应用程序。

Android依赖注入示例项目

我们的应用程序将包含一个单一活动,其中我们将保存并从EditTexts中检索数据到SharedPreferences中。

首先,在build.gradle文件中添加以下依赖项。

compile 'com.google.dagger:dagger:2.10'
annotationProcessor 'com.google.dagger:dagger-compiler:2.10'

让我们分析一下如何使用Dagger和上述注释在我们的应用程序中引入依赖注入的功能。

要在另一个类中实例化SharedPreferences实例,我们需要传递当前上下文。
组件完成将东西传递给Module类的工作,并将依赖项注入到我们的Activity中。

Android Dagger 2代码

首先让我们看一下" activity_main.xml"布局。

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
  xmlns:app="https://schemas.android.com/apk/res-auto"
  xmlns:tools="https://schemas.android.com/tools"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  tools:context="com.theitroad.dagger2.MainActivity">

  <EditText
      android:id="@+id/inUsername"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:layout_margin="8dp"
      android:hint="Username" 

  <EditText
      android:id="@+id/inNumber"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:layout_below="@+id/inUsername"
      android:layout_margin="8dp"
      android:inputType="number"
      android:hint="Number" 

  <Button
      android:id="@+id/btnSave"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="SAVE"
      android:layout_below="@+id/inNumber"
      android:layout_toLeftOf="@+id/btnGet"
      android:layout_toStartOf="@+id/btnGet"
      android:layout_marginRight="8dp"
      android:layout_marginEnd="8dp" 

  <Button
      android:id="@+id/btnGet"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="GET"
      android:layout_below="@+id/inNumber"
      android:layout_alignRight="@+id/inNumber"
      android:layout_alignEnd="@+id/inNumber" 

</RelativeLayout>

下面给出了SharedPrefModule.java类的代码。

package com.theitroad.dagger2;

import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;

@Module
public class SharedPrefModule {
  private Context context;

  public SharedPrefModule(Context context) {
      this.context = context;
  }

  @Singleton
  @Provides
  public Context provideContext() {
      return context;
  }

  @Singleton
  @Provides
  public SharedPreferences provideSharedPreferences(Context context) {
      return PreferenceManager.getDefaultSharedPreferences(context);
  }
}

上下文是在构造函数中设置的。
返回依赖对象的方法通常以provider开头,因此命名为" provideSharedPreferences()"。

下面给出了MyComponent.java接口的代码。

package com.theitroad.dagger2;

import javax.inject.Singleton;
import dagger.Component;

@Singleton
@Component(modules = {SharedPrefModule.class})
public interface MyComponent {
  void inject(MainActivity activity);
}

在@Component批注中提到了所有模块。
允许请求模块声明的依赖项的活动,服务或者片段(使用@Inject)应在此接口中使用单独的inject()方法进行声明。

下面给出了MainActivity.java类的代码:

package com.theitroad.dagger2;

import android.content.SharedPreferences;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;

import javax.inject.Inject;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

  EditText inUsername, inNumber;
  Button btnSave, btnGet;
  private MyComponent myComponent;
  @Inject
  SharedPreferences sharedPreferences;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);

      initViews();
      myComponent = DaggerMyComponent.builder().sharedPrefModule(new SharedPrefModule(this)).build();
      myComponent.inject(this);

  }

  private void initViews() {
      btnGet = findViewById(R.id.btnGet);
      btnSave = findViewById(R.id.btnSave);
      inUsername = findViewById(R.id.inUsername);
      inNumber = findViewById(R.id.inNumber);
      btnSave.setOnClickListener(this);
      btnGet.setOnClickListener(this);
  }

  @Override
  public void onClick(View v) {
      switch (v.getId()) {

          case R.id.btnGet:
              inUsername.setText(sharedPreferences.getString("username", "default"));
              inNumber.setText(sharedPreferences.getString("number", "12345"));
              break;
          case R.id.btnSave:
              SharedPreferences.Editor editor = sharedPreferences.edit();
              editor.putString("username", inUsername.getText().toString().trim());
              editor.putString("number", inNumber.getText().toString().trim());
              editor.apply();
              break;

      }
  }
}

将Dagger绑定到我们的应用程序中的代码是:

myComponent = DaggerMyComponent.builder().sharedPrefModule(new SharedPrefModule(this)).build();

" Dagger"关键字位于组件类名称的前面。
如果组件类名称为AppComponent,则结果为DaggerAppComponent。

将模块类名称转换为驼峰格式,并通过传递上下文在模块内部对其进行初始化(最终,SharedPreferences要求使用它进行初始化!)。

在此阶段,Dagger可以生成文件,因此您需要重建项目以使其能够执行其工作("构建">"重建项目")。

在此之后可以使用SharedPreferences依赖项对象:myComponent.inject(this)

我们可以对SharedPreferences进行操作,而无需在Activity中对其进行初始化。

我们可以对Retrofit适配器等做同样的事情,以使我们的代码更简单,更易于使用/测试。

上述应用程序的输出如下:

代替@Singleton,我们也可以定义不同的范围,例如@ PerApp,@ PerActivity,以使实例创建的数量分别取决于Application/Activity的生存期。