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);
这样,这块代码的流程如下:
但是如果我们是依赖第三方库的话,第三方库有可能是没有@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();
}
}
同样可以整理出一份流程:
以上就是Dagger2的最基本的知识点。