回顾
在上一篇中, 我们从源码的角度分析了Dagger的工作原理, 在这一篇中, 我们来重点讲解Dagger2依赖组织的方式, 在讲解之前, 我们先来理解Component dependencies和SubComponent
Component dependencies VS SubComponent
为了提高一个Component中代码的复用度, 我们可以利用 Component dependencies 和 SubComponent来获取Component中的依赖, 那么这两种方式有什么区别?
Component dependencies
我们还是用前两篇中的Demo来讲解, 首先我们定义一个AppComponent和AppModule, 并且在AppModule提供一个Student的实例
1 2 3 4 5 6 7 8
| @Singleton @Component(modules = {AppModule.class}) public interface AppComponent { Application providesApp(); Student providesStudent(); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| @Module public class AppModule { private final Application application; public AppModule(Application application) { this.application = application; } @Provides @Singleton Application providesApplication() { return application; } @Provides @Singleton Student providesStudent() { return new Student(); } }
|
现在我们有另外一个StudentComponent和StudentModule, 其中StudentModule需要Student实例, 由于我们在AppModule已经提供了Student实例, 我们可以在StudentComponent中定义dependencies, 这样就可以实现复用Student实例了
1 2 3 4 5
| @PerActivity @Component(dependencies = AppComponent.class, modules = StudentModule.class) public interface StudentComponent { void inject(StudentActivity activity); }
|
1 2 3 4 5 6 7 8 9
| @Module public class StudentModule { @Provides @PerActivity Data providesData() { return new Data(); } }
|
这就是Component dependencies的使用
SubComponent
SubComponent也可以实现Component的代码复用, 我们具体来看看怎么使用
1 2 3 4 5 6 7 8 9 10 11
| @Singleton @Component(modules = {AppModule.class}) public interface AppComponent { StudentComponent getComponent(); Application providesApp(); }
|
1 2 3 4 5
| @PerActivity @Subcomponent(modules = StudentModule.class) public interface StudentComponent { void inject(StudentActivity activity); }
|
两者区别?
既然两者都可以实现Component代码的复用, 那么他们的区别的是什么?
- Component dependencies方式的代码复用, 父Component必须显式暴露依赖给 dependencies的Component, 如上面的AppComponent
1
| Student providesStudent();
|
显式暴露了Student依赖给dependencies的Component, 而对于SubComponent不用显式暴露, 直接在父Component定义一个返回SubComponent的方法, 并在对应的类标注SubComponent注解
- 从设计的角度来看, SubComponent是为了让两个Component更具有内聚性. 而Component dependencies则是让两个Component彼此独立
源码读解
为什么 Component dependencies方式必须要显式暴露? 且听我道来
1 2 3 4 5 6 7 8 9
| @Override public Application providesApp() { return providesApplicationProvider.get(); } @Override public Student providesSyudent() { return providesStudentProvider.get(); }
|
上面两个方法是AppComponent具体实现类的重写的两个方法, 在AppComponent暴露方法的主要目的是在对应的实现类提供依赖, 而在依赖于AppComponent的StudentComponent中,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
| public final class DaggerStudentComponent implements StudentComponent { private Provider<Student> providesSyudentProvider; private Provider<Data> providesDataProvider; private MembersInjector<StudentActivity> studentActivityMembersInjector; private DaggerStudentComponent(Builder builder) { assert builder != null; initialize(builder); } public static Builder builder() { return new Builder(); } @SuppressWarnings("unchecked") private void initialize(final Builder builder) { this.providesSyudentProvider = new dagger.internal.Factory<Student>() { private final AppComponent appComponent = builder.appComponent; @Override public Student get() { return Preconditions.checkNotNull( appComponent.providesSyudent(), "Cannot return null from a non-@Nullable component method"); } }; this.providesDataProvider = DoubleCheck.provider(StudentModule_ProvidesDataFactory.create(builder.studentModule)); this.studentActivityMembersInjector = StudentActivity_MembersInjector.create(providesSyudentProvider, providesDataProvider); } @Override public void inject(StudentActivity activity) { studentActivityMembersInjector.injectMembers(activity); } public static final class Builder { private StudentModule studentModule; private AppComponent appComponent; private Builder() {} public StudentComponent build() { if (studentModule == null) { this.studentModule = new StudentModule(); } if (appComponent == null) { throw new IllegalStateException(AppComponent.class.getCanonicalName() + " must be set"); } return new DaggerStudentComponent(this); } public Builder studentModule(StudentModule studentModule) { this.studentModule = Preconditions.checkNotNull(studentModule); return this; } public Builder appComponent(AppComponent appComponent) { this.appComponent = Preconditions.checkNotNull(appComponent); return this; } } }
|
我们看看内部类Builder中, 有AppComponent成员变量, 因为我们依赖了AppComponent, 所以在创建StudentComponent的实例时, Builder强制要求我们调用 appComponent(AppComponent)方法时把StudentComponent依赖的AppComponent实例传递进来, 接下来, Student实例会通过传递进来的appComponent实例, 进行赋值
我们来看看SubComponent为什么不用显式暴露
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| private final class StudentComponentImpl implements StudentComponent { private final StudentModule studentModule; private Provider<Data> providesDataProvider; private MembersInjector<StudentActivity> studentActivityMembersInjector; private StudentComponentImpl() { this.studentModule = new StudentModule(); initialize(); } @SuppressWarnings("unchecked") private void initialize() { this.providesDataProvider = DoubleCheck.provider(StudentModule_ProvidesDataFactory.create(studentModule)); this.studentActivityMembersInjector = StudentActivity_MembersInjector.create( DaggerAppComponent.this.providesStudentProvider, providesDataProvider); } @Override public void inject(StudentActivity activity) { studentActivityMembersInjector.injectMembers(activity); } }
|
上面的StudentComponentImpl是StudentComponent具体实现类, 它是AppComponent实现类的内部类, 这样要实现复用就不用显式暴露接口了, 利用内部类的特性直接访问
经过我们上面分析, 我们也可以回答设计的区别了, 因为SubComponent对应的实现类会作为父Component的内部类, 这样两个Component的内聚性也就增强了, 而dependencies则会生成两个对应的分开的两个类, 通过暴露接口和传递实例进行沟通, 从而达到两个Component独立分离的目的
两者的使用场景
- 如果你想让两个Component的内聚性更强, 你应该用SubComponent
- 如果你想让两个Component彼此独立分离, 你应该用dependencies
组织方式
利用Singleton标注AppComponent
Singleton实现单例必须保证Component只会被初始化一次, 那么我们可以把需要定义成单例的类都定义在AppModule, 并且在AppComponent暴露对应单例给依赖的Component, 并在Application初始化AppComponent, 这样就保证了AppComponent只初始化一次
自定义Scope标注Activity
为了更好的组织项目结构, 我们可以定义PerAc(名字随意)注解, 标注基类ActivityComponent并且依赖与AppComponent, 接下来, 每个Activity可以定义对应的XXActivityComponent, 都让每个XXXActivity继承基类ActivityComponent, 并且依赖于AppComponent
SubComponent
我们的Activity可以能会有Fragment, 那Fragment对应的Component是要依赖于AppComponent还是做为对应ActivityComponent的SubComponent?
由于Fragment是包含在Activity中的, 更好的做法是将FragmentComponent作为对应的ActivtyComponent的SubComponent
告别Dagger2
写了三篇Dagger2的文章, 总算对Dagger2了解多了, 现在用得了很踏实. 所以该暂时告别Dagger2的学习了, 不过, 最后我还准备了一个Dagger2的使用的Demo, 这个Demo准备用MVP + Dagger2 + RxJava实现一个知乎日报, 嘻嘻, 如果你喜欢的话, 手抖给个star咯,
Demo在项目地址的RxJava+MVP+Dagger2分支中
项目地址
参考资料
https://guides.codepath.com/android/Dependency-Injection-with-Dagger-2#setup
http://jellybeanssir.blogspot.jp/2015/05/component-dependency-vs-submodules-in.html
http://stackoverflow.com/questions/29587130/dagger-2-subcomponents-vs-component-dependencies
https://google.github.io/dagger/users-guide.html