Dagger
是一个依赖注入框架, 它的核心实现原理是在编译期产生依赖注入相关代码, 我们可以通过Dagger
提供的注解来描述我们的依赖注入需求。
为了实现依赖注入,Dagger
需要知道对象的创建方式, 开发者需要知道怎么获取Dagger
创建的对象, 本文会围绕这两点介绍一下Dagger
中的注解的功能以及实现原理。
依赖注入 : 就是非自己主动初始化依赖(对象),而是通过外部传入依赖的方式。
Dagger 基础
@Inject
它既可以用来指明对象的依赖,也可以用来指明依赖对象的创建方式, 不同的用法Dagger
会在编译期生成不同的辅助类来完成依赖实例的注入 :
声明在成员变量上
class StudentTest {
@Inject
lateinit var nameInfo: NameInfo
}
复制代码
Dagger
会在编译期生成对应的依赖对象注入类(StudentTest_MembersInjector
),在运行时它用来给StudentTest
对象的nameInfo
注入NameInfo
实例:
public final class StudentTest_MembersInjector implements MembersInjector<StudentTest> {
private final Provider<NameInfo> nameInfoProvider;
public StudentTest_MembersInjector(Provider<NameInfo> nameInfoProvider) {
this.nameInfoProvider = nameInfoProvider;
}
public static MembersInjector<StudentTest> create(Provider<NameInfo> nameInfoProvider) {
return new StudentTest_MembersInjector(nameInfoProvider);}
@Override
public void injectMembers(StudentTest instance) {
injectNameInfo(instance, nameInfoProvider.get());
}
public static void injectNameInfo(StudentTest instance, NameInfo nameInfo) {
instance.nameInfo = nameInfo;
}
}
复制代码
Provider<NameInfo>
创建NameInfo
的模板接口
声明在构造函数上
class Student @Inject constructor(val nameInfo: NameInfo) : IPeople
复制代码
Dagger
会在编译期生成Student_Factory
类, 这个类会依赖构造参数来构造Student
对象:
public final class Student_Factory implements Factory<Student> {
private final Provider<NameInfo> nameInfoProvider;
...
public static Student_Factory create(Provider<NameInfo> nameInfoProvider) {
return new Student_Factory(nameInfoProvider);
}
public static Student newInstance(NameInfo nameInfo) {
return new Student(nameInfo);
}
}
复制代码
如果构造参数上标记了
@Inject
,那么Dagger
会先寻找这个参数的XX_Factory
,创建这个参数对象,然后再创建目前对象
@Module
它用来封装创建对象实例的方法:
@Inject
的方式散落在各处不好管理
@Module
class StudentModule {
@Provides
fun provideNameInfo() = NameInfo("wang", "pengcheng")
}
复制代码
@Provides
需要声明在@Module
标注的类中,它用来指明依赖实例的创建方式,对于每一个@Provides
标注的方法, Dagger
会在编译期生成对应的Factory
:
StudentModule_ProvideNameInfoFactory
public final class StudentModule_ProvideNameInfoFactory implements Factory<NameInfo> {
private final StudentModule module;
...
public static NameInfo provideNameInfo(StudentModule instance) {
return Preconditions.checkNotNull(instance.provideNameInfo(), "Cannot return null from a non-@Nullable @Provides method");
}
}
复制代码
即通过StudentModule().provideNameInfo()
创建对应的NameInfo
实例。
@Component
管理依赖实例, 链接@Inject
和@Module
, 可以为对象注入依赖实例 :
@Component(modules = [StudentModule::class])
interface StudentComponent {
fun inject(studentTest: StudentTest)
}
复制代码
StudentComponent
会收集modules = [StudentModule::class]
中依赖的创建方式,并通过这些方式创建对象实例赋值给StudentTest
需要的成员变量。
Dagger
会在编译期为这个接口生成对应的实现类DaggerStudentComponent
,这个类实现了StudentTest
的依赖注入:
public final class DaggerStudentComponent implements StudentComponent {
private final StudentModule studentModule;
private DaggerStudentComponent(StudentModule studentModuleParam) {
this.studentModule = studentModuleParam;
}
...
@Override
public void inject(StudentTest studentTest) {
injectStudentTest(studentTest);}
@Override
public NameInfo provideNameInfo() {
return StudentModule_ProvideNameInfoFactory.provideNameInfo(studentModule);}
private StudentTest injectStudentTest(StudentTest instance) {
StudentTest_MembersInjector.injectNameInfo(instance, StudentModule_ProvideNameInfoFactory.provideNameInfo(studentModule));
return instance;
}
public static final class Builder {
...
}
}
复制代码
DaggerStudentComponent
私有化构造函数,通过Builder
来创建, 创建时需要传入StudentModule
对象
暴露依赖实例
可以在@Component
添加方法来暴露依赖实例:
@Component(modules = [ClassroomModule::class])
interface ClassroomComponent {
...
fun getTeacher():Teacher
}
复制代码
Dagger
会生成工厂方法创建Teacher
实例, 这样在代码中就可以直接获取:
val teacher = DaggerClassroomComponent.builder().classroomModule(ClassroomModule()).build().getTeacher()
复制代码
Dagger的简单使用
通过对上面三大金刚的介绍, 我们了解了Dagger
的基本使用与实现原理, 在编码时就可以这样使用:
class StudentTest {
@Inject
lateinit var nameInfo: NameInfo
constructor() {
DaggerStudentComponent.builder().studentModule(StudentModule()).build().inject(this)
Log.d("dagger-test", "studentName : ${nameInfo.first} ${nameInfo.last}")
}
}
复制代码
logcat输出:
D/dagger-test: wang pengcheng
复制代码
@Binds
它也可以像@Provides
指明一个依赖实例的提供方式, 不过它只能声明在抽象方法上, 它用来告诉Dagger
接口应采用哪种实现:
@Module(includes = [StudentModule::class])
abstract class ClassroomModule {
@Binds
abstract fun bindPeopleWithStudent(test: Student): IPeople
}
复制代码
@Module(includes = [StudentModule::class])
可以使ClassroomModule
拥有StudentModule
创建依赖实例的能力
fun bindPeopleWithStudent(test: Student): IPeople
定义 IPeople
的实现类为Student
对于
Student
实例的提供可以使用@Provides
,也可以使用@Inject
标注在构造方法上 :
class Student @Inject constructor(val nameInfo: NameInfo) : IPeople
复制代码
@Binds
解决了面向接口编程的需求, 即指明了依赖接口的实现类。当然这种情况也可以用@Provides
实现(方法实体是类型的强转), 但@Binds明显更加清晰 :
@Provides
fun providePeopleWithStudent() = Student(provideNameInfo()) as IPeople
复制代码
Component依赖
如果ClassroomComponent
需要使用StudentComponent
的依赖实例, 则可以这样写:
@Component(modules = [ClassroomModule::class], dependencies = [StudentComponent::class])
interface ClassroomComponent {
fun inject(test: ClassroomTest)
}
@Component(modules = [StudentModule::class])
interface StudentComponent {
fun provideNameInfo(): NameInfo //传递到依赖他的Component
}
复制代码
StudentComponent
可以通过StudentModule
提供NameInfo
, ClassroomComponent
通过dependencies = [StudentComponent::class]
来使用NameInfo
, 除了dependencies = [StudentComponent::class]
外, 还需要StudentComponent
暴露对应的依赖实例方法fun provideNameInfo(): NameInfo
上面经过Dagger
编译会生成:
public final class DaggerClassroomComponent implements TestComponent {
private final StudentComponent studentComponent;
private ClassroomTest injectClassroomTest(ClassroomTest instance) {
ClassroomTest_MembersInjector.injectStudent(instance, getStudent());
return instance;
}
}
public final class DaggerStudentComponent implements StudentComponent {
@Override
public NameInfo provideNameInfo() {
return StudentModule_ProvideNameInfoFactory.provideNameInfo(studentModule);
}
}
复制代码
即StudentComponent
变成了DaggerTestComponent
的成员变量,这样就可以为Test
注入NameInfo
依赖, 使用时:
class ClassroomTest {
@Inject
lateinit var student: IPeople
constructor() {
DaggerClassroomComponent.builder().studentComponent(DaggerStudentComponent.builder().studentModule(StudentModule()).build()).build().inject(this)
Log.d("dagger-test", "studentName : ${student.getName()}")
}
}
复制代码
Subcomponent
上面
dependencies = [XXXComponent::class]
可以简单的理解为:AComponent
把BComponent
变成成员变量, 然后使用BComponent
其依赖注入的能力
@Subcomponent
则可以使BComponent
变为AComponent
的内部类,然后使用AComponent
的依赖注入能力(@Module
):
AComponent:
@Component(modules = [ClassroomModule::class])
interface ClassroomComponent {
fun inject(test: ClassroomTest)
fun studentComponent(): StudentComponent.Builder //用来构造StudentComponent
}
复制代码
@Module(subcomponents = [StudentComponent::class])
class ClassroomModule {
@Provides
fun provideTeacher() = Teacher()
}
复制代码
subcomponents = [StudentComponent::class]
表示StudentComponent
可以看到ClassroomModule
提供的依赖实例 :
BComponent:
@Subcomponent(modules = [StudentModule::class])
interface StudentComponent {
fun inject(studentTest: StudentTest) //StudentTest对象依赖Teacher实例
@Subcomponent.Builder
interface Builder {
fun build(): StudentComponent
}
}
复制代码
使用@Subcomponent
声明子Component
, 还需要显示声明Builder
, 这样父组件才知道如何创建子组件
上面经过Dagger
编译后不会生成DaggerStudentComponent
, 只会生成DaggerClassroomComponent
:
public final class DaggerClassroomComponent implements ClassroomComponent {
private final class StudentComponentBuilder implements StudentComponent.Builder {
@Override
public StudentComponent build() {
return new StudentComponentImpl(new StudentModule());
}
}
private final class StudentComponentImpl implements StudentComponent {
private final StudentModule studentModule;
private StudentComponentImpl(StudentModule studentModuleParam) {
this.studentModule = studentModuleParam;
}
@Override
public void inject(StudentTest studentTest) {
injectStudentTest(studentTest);}
private StudentTest injectStudentTest(StudentTest instance) {
StudentTest_MembersInjector.injectNameInfo(instance, StudentModule_ProvideNameInfoFactory.provideNameInfo(studentModule));
StudentTest_MembersInjector.injectTeacher(instance, ClassroomModule_ProvideTeacherFactory.provideTeacher(DaggerClassroomComponent.this.classroomModule));
return instance;
}
}
}
复制代码
可以看到StudentTest injectStudentTest(StudentTest instance)
方法中使用了ClassroomModule
提供的依赖实例方法。
因为没有生成
DaggerStudentComponent
,所以对于DaggerStudentComponent
的构建必须这样做 :
class StudentTest {
@Inject lateinit var nameInfo: NameInfo
@Inject lateinit var teacher: Teacher
constructor() {
DaggerClassroomComponent.builder().classroomModule(ClassroomModule()).build().studentComponentBuilder().build().inject(this)
Log.d("dagger-test", "teacher name : ${teacher.name}")
}
}
复制代码
@Scope
@Scope
在Dagger
中和@Component
紧紧相连 : 如果一个@Module
提供的依赖实例声明了和@Component
相同的@Scope
,那么这个@Component
会使用同一个依赖实例来做依赖注入 :
@Singleton
@Component(modules = [SingletonModule::class])
interface AppComponent : AndroidInjector<TestApplication> {
fun inject(app: Application)
fun getClassroom():Classroom
}
复制代码
你也可以直接手动获取单例
Classroom
@Module
class SingletonModule {
@Singleton
@Provides
fun provideClassRoom() = Classroom()
}
复制代码
@Singleton是
Dagger
内置的@Scope
,一般用来定义一个@Component
内唯一的依赖实例
class Test {
...
@Inject lateinit var room1: Classroom
@Inject lateinit var room2: Classroom
...
}
复制代码
上面这个Test
的两个成员变量其实引用的是同一个Classroom
实例, 不过在使用@Scope
是需要注意 : @Subcomponent
不能和@Component
声明相同的@Scope
单例的实现原理
其实看一下Component
的注入实现就明白了:
private ClassroomActivity injectClassroomActivity(ClassroomActivity instance) {
ClassroomActivity_MembersInjector.injectTeacher1(instance, provideTeacherProvider.get());
ClassroomActivity_MembersInjector.injectTeacher2(instance, provideTeacherProvider.get());
return instance;
}
复制代码
即使用同一个Provider<T>
来获取的对象
Dagger in Android
上面介绍了Dagger
的基本原理与使用方法, 不过在Android中如何使用Dagger
呢?
如果按照上面的基本用法使用Dagger
则会遇到一些列的问题, 比如像Activity/Fragment
一般是由系统创建的, 所以我们不能把它变成依赖实例, 也不能完成自动依赖注入, 因此我们需要写出类似下面这种代码 :
class ClassroomActivity : Activity() {
@Inject lateinit var teacher: Teacher
override fun onCreate(savedInstanceState: Bundle?) {
ClassroomActivityComponent.builder().build().inject(this);
}
}
复制代码
这样写有两点不好:
- 太多类似的代码
- 每个
Activity/Fragment
在注入是都需要知道其对应的DaggerXXXComponent
怎么解决呢?Dagger
官方给出的实现步骤如下:
Activity的自动注入
-
顶层Component绑定AndroidInjectionModule
-
定义一个
@Subcomponent
并继承自AndroidInjector<T>
@Subcomponent(modules = [ClassroomModule::class])
interface ClassroomActivitySubcomponent : AndroidInjector<ClassroomActivity> {
@Subcomponent.Factory
interface Factory : AndroidInjector.Factory<ClassroomActivity>
}
复制代码
- 定义一个
@Module
并绑定先前定义的@Subcomponent
@Module(subcomponents = [ClassroomActivitySubcomponent::class])
abstract class ClassroomActivityModule {
@Binds
@IntoMap
@ClassKey(ClassroomActivity::class)
abstract fun bindClassroomActivityFactory(factory: ClassroomActivitySubcomponent.Factory): AndroidInjector.Factory<*>
}
复制代码
- 把上面定义的
@Module
绑定到@Component
@Component(modules = [AndroidInjectionModule::class, ClassroomActivityModule::class])
interface AppComponent : AndroidInjector<TestApplication> {
fun inject(app: Application)
}
复制代码
- Application初始化
DispatchingAndroidInjector
class TestApplication : Application(), HasAndroidInjector {
@Inject
lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Any>
override fun onCreate() {
super.onCreate()
DaggerAppComponent.create().inject(this)
}
override fun androidInjector() = dispatchingAndroidInjector
}
复制代码
- 在Activity中注入依赖
class ClassroomActivity : Activity() {
@Inject
lateinit var teacher: Teacher
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
AndroidInjection.inject(this)
setContentView(TextView(this).apply {
text = "${teacher.nameInfo.getName()} "
})
}
}
复制代码
如果你不想每次都写
AndroidInjection.inject(this)
, 你可以直接让你的Activity
继承自DaggerAcitivity
AndroidInjection.inject(this)
会自动寻找AppComponent
中的依赖实例并注入到ClassroomActivity
中。
对于上面2、3两步,其实可以使用@ContributesAndroidInjector
合为一步:
@Module
abstract class ClassroomActivityModule {
@ContributesAndroidInjector(modules = [ClassroomModule::class])
abstract fun contributeInjectorClassroomActivity(): ClassroomActivity
}
复制代码
Dagger
在编译时会根据@ContributesAndroidInjector
自动生成上面2、3步的代码。
那上面实现原理是什么呢?
Activity自动注入实现原理
来看一下DaggerAppComponent
中生成的依赖注入代码:
public final class DaggerAppComponent implements AppComponent {
...
private Map<Class<?>, Provider<AndroidInjector.Factory<?>>> getMapOfClassOfAndProviderOfAndroidInjectorFactoryOf() {
return Collections.<Class<?>, Provider<AndroidInjector.Factory<?>>>singletonMap(ClassroomActivity.class, (Provider) classroomActivitySubcomponentFactoryProvider);
}
@SuppressWarnings("unchecked")
private void initialize() {
this.classroomActivitySubcomponentFactoryProvider = new Provider<ClassroomActivityModule_ContributeInjectorClassroomActivity.ClassroomActivitySubcomponent.Factory>() {
@Override
public ClassroomActivityModule_ContributeInjectorClassroomActivity.ClassroomActivitySubcomponent.Factory get(
) {
return new ClassroomActivitySubcomponentFactory();}
};
}
...
private final class ClassroomActivitySubcomponentImpl implements ClassroomActivityModule_ContributeInjectorClassroomActivity.ClassroomActivitySubcomponent {
...
@Override
public void inject(ClassroomActivity arg0) {injectClassroomActivity(arg0);}
private ClassroomActivity injectClassroomActivity(ClassroomActivity instance) {
ClassroomActivity_MembersInjector.injectTeacher(instance, getTeacher());
return instance;
}
}
}
复制代码
上面这段逻辑核心点有两个:
ClassroomActivity
是利用ClassroomActivitySubcomponentImpl
完成依赖注入的ClassroomActivitySubcomponentImpl
保存在了DaggerAppComponent.getMapOfClassOfAndProviderOfAndroidInjectorFactoryOf
的map中
然后继续看一下AndroidInjection.inject(this)
发生了什么:
public final class AndroidInjection {
public static void inject(Activity activity) {
Application application = activity.getApplication();
...
injector = ((HasAndroidInjector) application).androidInjector();
...
injector.inject(activity);
}
}
复制代码
即它最终调用了activity.getApplication().androidInjector().injector.inject(activity)
,其实就是调用到了DispatchingAndroidInjector<Any>.inject()
,而它最终会调用到:
public boolean maybeInject(T instance) {
Provider<AndroidInjector.Factory<?>> factoryProvider =
injectorFactories.get(instance.getClass().getName());
AndroidInjector.Factory<T> factory = (AndroidInjector.Factory<T>) factoryProvider.get();
...
factory.create(instance).inject(instance);
}
复制代码
其实上面injectorFactories
就是DaggerAppComponent
中的那个Factory Map
, 最终调用到ClassroomActivitySubcomponentImpl.inject()