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()