Android Dagger2 基础 一(@Inject, @Component, @Module & @Provider)

1,590 阅读6分钟

Android Dagger2

前言

在做项目的时候,需要向SystemUI的代码中追加自己的代码,实现一个代替原生StatusBar的功能。

前辈在做这个项目的时候是在Android8.0的系统中做的,我需要把这个功能移植到Android10.0。

在移植代码的时候,发现AOSP中新增加了一个类Dependency(packages/systemui/src/com/android/systemui/Dependency.java)。

查看Dependency中的代码,根据相关的代码注释,才知道这块功能是基于Dagger2实现的。

Dagger2在SystemUI中的使用

关于Dagger2在SystemUI中的使用,在源码中是有相关的说明文档的(packages/systemui/docs/dagger.md)。在该文档中详细的介绍了Dagger2在SystemUI中的使用。

Dagger2的基础说明

什么是依赖注入 Dependency Injection

依赖注入是实现控制反转(IoC)的一个方案。

控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫“依赖查找”(Dependency Lookup)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体,将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。

所以 IoC 不是技术,而是思想,利用这种思想可以降低对象间的耦合,提高代码重用率。

通俗的来讲呢,就是一个类中需要依赖其他对象时,不需要你亲自为那些需要依赖的对象赋值,而是将为那些对象赋值的操作交给了IOC框架。

Dagger2在AndroidStudio中的配置

AndroidStudio的版本是4.0.2

只需要在Module:app的build.gradle的dependencies中追加如下的配置项(最后三行配置):

dependencies {
    implementation fileTree(dir: "libs", include: ["*.jar"])
    implementation 'androidx.appcompat:appcompat:1.2.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.0.2'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.2'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
    //引入dagger2
    implementation 'com.google.dagger:dagger:2.4'
    annotationProcessor 'com.google.dagger:dagger-compiler:2.4'
    //java注解
    compileOnly 'org.glassfish:javax.annotation:10.0-b28'
}

Dagger2的关键字的说明

查看Dependency.java的代码,可以看到如下的关键字:

①@Inject

主要有两个作用
1 作为依赖注提供方: 使用@Inject注解构造方法。 注解类的构造函数,让Dagger2帮我们实例化该类,并注入。
2 作为依赖需求方: 使用@Inject注解成员。 如果一个成员变量被@Inject注解修饰,并且成员类的构造函数也被@Inject注解,那么dagger2帮我们实例化该成员类,并注入。
通常在需要依赖的地方使用这个注解。换句话说,你用它告诉Dagger这个类或者字段需要依赖注入。这样,Dagger就会构造一个这个类的实例并满足他们的依赖。

使用@Inject可以让IoC容器负责生成instance,如果没有这个注解,dagger将不认识,当做普通类,无法代理

示例:

// **************ClassRoom.java**************
public class ClassRoom {
    @Inject
    public ClassRoom() {
        Log.d("Test", "This is a Constructors");
    }
​
    @NonNull
    @Override
    public String toString() {
        return "This is a ClassRoom";
    }
}
​
// **************MainActivity.java**************
public class MainActivity extends AppCompatActivity {
    private TextView mTextView;
    @Inject ClassRoom mClassRoom;
​
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mTextView = (TextView) findViewById(R.id.text);
        mTextView.setText("This is school and has have " + mClassRoom);
    }
}

根据上面的代码,使用AndroidStudio的Make Project功能,编译出来的文件如下:

//**************ClassRoom_Factory.java**************
@Generated(
  value = "dagger.internal.codegen.ComponentProcessor",
  comments = "https://google.github.io/dagger"
)
public enum ClassRoom_Factory implements Factory<ClassRoom> {
  INSTANCE;
​
  @Override
  public ClassRoom get() {
    return new ClassRoom();
  }
​
  public static Factory<ClassRoom> create() {
    return INSTANCE;
  }
}
​
//**************MainActivity_MembersInjector.java**************
@Generated(
  value = "dagger.internal.codegen.ComponentProcessor",
  comments = "https://google.github.io/dagger"
)
public final class MainActivity_MembersInjector implements MembersInjector<MainActivity> {
  private final Provider<ClassRoom> mClassRoomProvider;
​
  public MainActivity_MembersInjector(Provider<ClassRoom> mClassRoomProvider) {
    assert mClassRoomProvider != null;
    this.mClassRoomProvider = mClassRoomProvider;
  }
​
  public static MembersInjector<MainActivity> create(Provider<ClassRoom> mClassRoomProvider) {
    return new MainActivity_MembersInjector(mClassRoomProvider);
  }
​
  @Override
  public void injectMembers(MainActivity instance) {
    if (instance == null) {
      throw new NullPointerException("Cannot inject members into a null reference");
    }
    instance.mClassRoom = mClassRoomProvider.get();
  }
​
  public static void injectMClassRoom(
      MainActivity instance, Provider<ClassRoom> mClassRoomProvider) {
    instance.mClassRoom = mClassRoomProvider.get();
  }
}

通过上面编译出来的代码,我们可以很明显的发现在ClassRoom_Factory.java文件中的get()方法实际就是new了一个ClassRoom()的构造函数。而在MainActivity_MembersInjector.java文件中,就是调用的ClassRoom_Factory的get方法。

只写了这些代码,然后运行,mClassRoom一直都是null,所以我们还需要添加一个桥梁来连接。而这个桥梁就是Component

@Component

Component 是一个注解类,且是一个接口或抽象类。因此必须对一个接口标记 @Component 才能获得一个 Component。它将获得一个目标类实例的引用(MainActivity),然后查找这个类所依赖的类(@Inject ClassRoom mClassRoom)。得到答案之后它会去查找是否有已知的构造函数(之前 @Inject 标注过的),然后实例化并注入到目标类。

示例:

//**************ActivityComponent.java**************
@Component
public interface ActivityComponent {
    void inject(MainActivity mainActivity);
}

查看ActivityComponent.java编译出来的代码:

@Generated(
  value = "dagger.internal.codegen.ComponentProcessor",
  comments = "https://google.github.io/dagger"
)
public final class DaggerActivityComponent implements ActivityComponent {
  private MembersInjector<MainActivity> mainActivityMembersInjector;
​
  private DaggerActivityComponent(Builder builder) {
    assert builder != null;
    initialize(builder);
  }
​
  public static Builder builder() {
    return new Builder();
  }
​
  public static ActivityComponent create() {
    return builder().build();
  }
​
  @SuppressWarnings("unchecked")
  private void initialize(final Builder builder) {
​
    this.mainActivityMembersInjector =
        MainActivity_MembersInjector.create(ClassRoom_Factory.create());
  }
​
  @Override
  public void inject(MainActivity MainActivity) {
    mainActivityMembersInjector.injectMembers(MainActivity);
  }
​
  public static final class Builder {
    private Builder() {}
​
    public ActivityComponent build() {
      return new DaggerActivityComponent(this);
    }
  }
}

根据上面的编译出来的文件代码结合MainActivity_MembersInjector.java代码,所以我们需要在MainActivity.java中追加如下的代码:

setContentView(R.layout.activity_main);
DaggerActivityComponent.builder().build().inject(this); // 追加
mTextView = (TextView) findViewById(R.id.text);

这样,这块代码的流程如下:

image.png   但是如果我们是依赖第三方库的话,第三方库有可能是没有@Inject的,而我们也不可能修改第三方库的代码,来添加@Inject的关键字,所以此时就需要使用@Module和@Provider。

@Module & @Provider

@Module 相当于给第三方库套一层封装,给他从外面包裹一个能够通知 Dagger 的创建方法。@Module和@Component配合使用的意思就是告诉注入器,如果你在实例化对象的时候,没有找到合适的构造函数,你就来我这里找。@Module通常标注一个类,该类里面可以实例化各种类,Component在注入对象的时候先去Module中找,如果找不到就会检查所有被@Inject标注的构造函数。

@Provider 用来标注一个方法,告诉注入器,我标注的方法你可以用来提供实例,表示向外提供依赖。方法的返回类型就是提供的依赖类型。

示例:

//**************模拟第三方的类 Teacher.java**************
public class Teacher {
    public Teacher() {
        Log.d("Test", "This is a teacher");
    }
​
    public String getTeacher1() {
        return "This is a teacher";
    }
​
    public String getTeacher2() {
        return "This is an other teacher";
    }
}
//**************TeacherModule.java**************
@Module
public class TeacherModule {
​
    @Provides
    public Teacher getTeacher() {
        return new Teacher();
    }
}
​
//**************ActivityComponent.java**************
@Component(modules = TeacherModule.class) //这句代码是需要追加的,作用就是声明该Component含有哪几个Module,当Component需要某个依赖对象时,就会通过这些Module类中对应的方法获取依赖对象
public interface ActivityComponent {
    void inject(MainActivity MainActivity);
}
​
//**************MainActivity.java**************
public class MainActivity extends AppCompatActivity {
    ......
    @Inject Teacher mTeacher;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ......
        mTextView.setText("This is school and has have \n" + mClassRoom + "\n" + mTeacher.getTeacher1() + "\n" + mTeacher.getTeacher2());
    }
}

根据上面的代码,查看AndroidStudio编译出来的代码:

//**************TeacherModule_GetTeacherFactory.java**************
@Generated(
  value = "dagger.internal.codegen.ComponentProcessor",
  comments = "https://google.github.io/dagger"
)
public final class TeacherModule_GetTeacherFactory implements Factory<Teacher> {
  private final TeacherModule module;
​
  public TeacherModule_GetTeacherFactory(TeacherModule module) {
    assert module != null;
    this.module = module;
  }
​
  @Override
  public Teacher get() {
    return Preconditions.checkNotNull(
        module.getTeacher(), "Cannot return null from a non-@Nullable @Provides method");
  }
​
  public static Factory<Teacher> create(TeacherModule module) {
    return new TeacherModule_GetTeacherFactory(module);
  }
}

//**************Factory.java**************
public interface Factory<T> extends Provider<T> {
}
​
//**************DaggerActivityComponent.java**************
@Generated(
  value = "dagger.internal.codegen.ComponentProcessor",
  comments = "https://google.github.io/dagger"
)
public final class DaggerActivityComponent implements ActivityComponent {
  private Provider<Teacher> getTeacherProvider;
​
  private MembersInjector<MainActivity> mainActivityMembersInjector;
​
  private DaggerActivityComponent(Builder builder) {
    assert builder != null;
    initialize(builder);
  }
​
  public static Builder builder() {
    return new Builder();
  }
​
  public static ActivityComponent create() {
    return builder().build();
  }
​
  @SuppressWarnings("unchecked")
  private void initialize(final Builder builder) {
​
    this.getTeacherProvider = TeacherModule_GetTeacherFactory.create(builder.teacherModule);
​
    this.mainActivityMembersInjector = MainActivity_MembersInjector.create(getTeacherProvider);
  }
​
  @Override
  public void inject(MainActivity MainActivity) {
    mainActivityMembersInjector.injectMembers(MainActivity);
  }
​
  public static final class Builder {
    private TeacherModule teacherModule;
​
    private Builder() {}
​
    public ActivityComponent build() {
      if (teacherModule == null) {
        this.teacherModule = new TeacherModule();
      }
      return new DaggerActivityComponent(this);
    }
​
    public Builder teacherModule(TeacherModule teacherModule) {
      this.teacherModule = Preconditions.checkNotNull(teacherModule);
      return this;
    }
  }
}
​
//**************MainActivity_MembersInjector.java**************
@Generated(
  value = "dagger.internal.codegen.ComponentProcessor",
  comments = "https://google.github.io/dagger"
)
public final class MainActivity_MembersInjector implements MembersInjector<MainActivity> {
  private final Provider<Teacher> mTeacherProvider;
​
  public MainActivity_MembersInjector(Provider<Teacher> mTeacherProvider) {
    assert mTeacherProvider != null;
    this.mTeacherProvider = mTeacherProvider;
  }
​
  public static MembersInjector<MainActivity> create(Provider<Teacher> mTeacherProvider) {
    return new MainActivity_MembersInjector(mTeacherProvider);
  }
​
  @Override
  public void injectMembers(MainActivity instance) {
    if (instance == null) {
      throw new NullPointerException("Cannot inject members into a null reference");
    }
    instance.mTeacher = mTeacherProvider.get();
  }
​
  public static void injectMTeacher(MainActivity instance, Provider<Teacher> mTeacherProvider) {
    instance.mTeacher = mTeacherProvider.get();
  }
}

同样可以整理出一份流程:

image.png

以上就是Dagger2的最基本的知识点。