Dagger 2学习与探索(八)
上一期介绍了Component dependency
方法,这一期介绍SubComponent
方法,效果是类似的,只是实现手段有一点不一样。
SubComponent
的声明不再是用@Component
标识,而是使用@Subcomponent
标识,不再有dependency
参数,同时上级Component
也无需再添加暴露函数。
那么SubComponent
是如何与上级建立联系的呢?
有两种方式:
- 在上级
Component
内添加获得该SubComponent
的抽象方法; - 从v2.7开始,允许在
SubComponent
内添加builder
,使用build()
方法来获取实例,同时还需要在上级Component
内添加SubComponent
的binder
的相关代码,来实现映射。这种方法坏处是复杂很多,好处是解耦?以我目前看来,虽然不用写工厂方法了,可还是要加更多新的东西到上级,代价实在不对等。不过既然这个功能在这里,还是了解一下比较好。
第一种方法
主体代码
和上一期的非常类似,稍微做一下修改:
AppComponent
不用再暴露ClassA
的方法,而加上ActivityComponent
的构造方法:
@PerApplication
@Component(modules = AppModule.class)
public interface AppComponent {
void inject(MyApp myApp);
ActivityComponent getActivityComponent(ActivityModule activityModule);
}
注意和提供/暴露函数不太一样,这里虽然ActivityComponent
自己有范围标注,但在这里不必标出。你也可以标出,但不会造成任何区别。ActivityComponent
不是注入对象,并不会变成单例模式。
@PerActivity
@Subcomponent(modules = ActivityModule.class)
public interface ActivityComponent {
void inject(MainActivity mainActivity);
}
Module
部分没有变化。然后就是使用的时候有区别:
public class MainActivity extends AppCompatActivity {
@Inject ClassB classB;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MyApp.getAppComponent().getActivityComponent(new ActivityModule(4)).inject(this);
}
}
现在ActivityComponent
是由AppComponent
产生。运行之前还是要rebuild
一下。
可以看到,这种方法和上一期的难易程度相差不大,不过由于Dagger1的时代就有类似Component dependency
的模式,因此可能Component dependency
用的更多一些。
生成代码
还是来看看DaggerAppComponent
吧。现在DaggerActivityComponent
已经不存在了。
public final class DaggerAppComponent implements AppComponent {
private Provider<Integer> provideIntAProvider;
private Provider<Integer> provideIntBProvider;
private Provider<ClassA> provideClassAProvider;
private MembersInjector<MyApp> myAppMembersInjector;
private DaggerAppComponent(Builder builder) {
assert builder != null;
initialize(builder);
}
public static Builder builder() {
return new Builder();
}
@SuppressWarnings("unchecked")
private void initialize(final Builder builder) {
this.provideIntAProvider = AppModule_ProvideIntAFactory.create(builder.appModule);
this.provideIntBProvider = AppModule_ProvideIntBFactory.create(builder.appModule);
this.provideClassAProvider =
DoubleCheck.provider(
AppModule_ProvideClassAFactory.create(
builder.appModule, provideIntAProvider, provideIntBProvider));
this.myAppMembersInjector = MyApp_MembersInjector.create(provideClassAProvider);
}
@Override
public void inject(MyApp myApp) {
myAppMembersInjector.injectMembers(myApp);
}
@Override
public ActivityComponent getActivityComponent(ActivityModule activityModule) {
return new ActivityComponentImpl(activityModule);
}
public static final class Builder {
private AppModule appModule;
private Builder() {}
public AppComponent build() {
if (appModule == null) {
throw new IllegalStateException(AppModule.class.getCanonicalName() + " must be set");
}
return new DaggerAppComponent(this);
}
public Builder appModule(AppModule appModule) {
this.appModule = Preconditions.checkNotNull(appModule);
return this;
}
}
private final class ActivityComponentImpl implements ActivityComponent {
private final ActivityModule activityModule;
private Provider<Integer> provideCProvider;
private Provider<ClassB> provideClassBProvider;
private MembersInjector<MainActivity> mainActivityMembersInjector;
private ActivityComponentImpl(ActivityModule activityModule) {
this.activityModule = Preconditions.checkNotNull(activityModule);
initialize();
}
@SuppressWarnings("unchecked")
private void initialize() {
this.provideCProvider = ActivityModule_ProvideCFactory.create(activityModule);
this.provideClassBProvider =
DoubleCheck.provider(
ActivityModule_ProvideClassBFactory.create(
activityModule, DaggerAppComponent.this.provideClassAProvider, provideCProvider));
this.mainActivityMembersInjector = MainActivity_MembersInjector.create(provideClassBProvider);
}
@Override
public void inject(MainActivity mainActivity) {
mainActivityMembersInjector.injectMembers(mainActivity);
}
}
}
可见其内部新生成了一个类,ActivityComponentImpl
,效果就像把上一期的DaggerActivityComponent
合并到这里来。
第二种方法
主体代码
ActivityComponent
@PerActivity
@Subcomponent(modules = ActivityModule.class)
public interface ActivityComponent {
void inject(MainActivity mainActivity);
@Subcomponent.Builder
interface Builder extends SubcomponentBuilder<ActivityComponent> {
Builder withActivityModule(ActivityModule activityModule);
}
}
现在AppComponent
没有工厂方法了,ActivityComponent
需要提供一个Builder
来用参数构造自己。注意到@Subcomponent.Builder
标注,看看究竟:
/**
* A subcomponent that inherits the bindings from a parent {@link Component} or
* {@link Subcomponent}. The details of how to associate a subcomponent with a parent are described
* in the documentation for {@link Component}.
*
* @author Gregory Kick
* @since 2.0
*/
@Retention(RUNTIME) // Allows runtimes to have specialized behavior interoperating with Dagger.
@Target(TYPE)
@Documented
public @interface Subcomponent {
/**
* A list of classes annotated with {@link Module} whose bindings are used to generate the
* subcomponent implementation. Note that through the use of {@link Module#includes} the full set
* of modules used to implement the subcomponent may include more modules that just those listed
* here.
*/
Class<?>[] modules() default {};
/**
* A builder for a subcomponent. This follows all the rules of {@link Component.Builder}, except
* it must appear in classes annotated with {@link Subcomponent} instead of {@code Component}.
* Components can have methods that return a {@link Subcomponent.Builder}-annotated type,
* allowing the user to set modules on the subcomponent using their defined API.
*/
@Target(TYPE)
@Documented
@interface Builder {}
}
正如注释文档描述的那样,Builder
接口需要有一个方法来帮助设置ActivityModule
,在这里就是withActivityModule
方法,名字仍旧不重要。
SubcomponentBuilder
是自己定义的一个接口,好在如果使用第二种方式,这个接口可以复用在其他Subcomponent
里:
public interface SubcomponentBuilder<V> {
V build();
}
名字不重要,重要的是要带有build()
方法。这个接口与Builder
配合来实现Builder.build()
。
AppComponent
@PerApplication
@Component(modules = {AppModule.class, ActivityBinders.class})
public interface AppComponent {
void inject(MyApp myApp);
Map<Class<?>, Provider<SubcomponentBuilder>> subcomponentBuilders();
}
首先介绍一下ActivityBinders
,这个负责获取对应的Builder
:
@Module(subcomponents={ ActivityComponent.class })
public abstract class ActivityBinders {
@Binds
@IntoMap
@SubcomponentKey(Builder.class)
public abstract SubcomponentBuilder getActivityBuilder(Builder impl);
}
注意其标注,属于@Module
而且要声明对应的Subcomponent
。
出现了三个新标注。
@Binds
说是新标注,其实在其他地方也见过。
@IntoMap
:
/**
* The method's return type forms the type argument for the value of a {@code Map<K, Provider<V>>},
* and the combination of the annotated key and the returned value is contributed to the map as a
* key/value pair. The {@code Map<K, Provider<V>>} produced from the accumulation of values will be
* immutable.
*
* @see <a href="https://google.github.io/dagger/multibindings#map-multibindings">Map multibinding</a>
*/
@Documented
@Target(METHOD)
@Retention(RUNTIME)
public @interface IntoMap {}
大意是标注的方法会和某个Map<K, Provider<V>>
建立联系,准确地说是返回类型和标注类型会成为一个键值对。
返回类型这里自然是SubcomponentBuilder
的实现类,标注类型就是下面@SubcomponentKey(Builder.class)
标注的Builder.class
,这个Builder
就是ActivityComponent
里面的Builder
。
而SubcomponentKey
也是要自己添加的一个标注接口,好在还是可以复用。
@MapKey
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface SubcomponentKey {
Class<?> value();
}
其实总的来说,现在AppComponent
不直接产生ActivityComponent
,而是生成一个Map,key
就是Builder
的类(由@SubcomponentKey规定,当然你也可以直接用@MapKey
,不过具体实现有点不同),value
自然就是对应的Builder
的Provider
封装的工厂。ActivityComponent
自己查表获取Builder
再传入ActivityModule
完成建造,从而再注入。
当然了,这里使用了一些帮助的扩展接口/标注,从而使得添加多个Subcomoponent
变得容易。不用也是完全可以的。
-
MainActivity
基于2的分析,这里的代码思路就很清晰了:
public class MainActivity extends AppCompatActivity {
@Inject ClassB classB;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ActivityComponent.Builder builder
= (ActivityComponent.Builder) MyApp.getAppComponent()
.subcomponentBuilders()
.get(ActivityComponent.Builder.class)
.get();
builder.withActivityModule(new ActivityModule(4)).build().inject(this);
}
}
生成代码
DaggerAppComponent
:
public final class DaggerAppComponent implements AppComponent {
private Provider<Integer> provideIntAProvider;
private Provider<Integer> provideIntBProvider;
private Provider<ClassA> provideClassAProvider;
private MembersInjector<MyApp> myAppMembersInjector;
private Provider<ActivityComponent.Builder> activityComponentBuilderProvider;
private Provider<SubcomponentBuilder> getActivityBuilderProvider;
private Provider<Map<Class<?>, Provider<SubcomponentBuilder>>>
mapOfClassOfAndProviderOfSubcomponentBuilderProvider;
private DaggerAppComponent(Builder builder) {
assert builder != null;
initialize(builder);
}
public static Builder builder() {
return new Builder();
}
@SuppressWarnings("unchecked")
private void initialize(final Builder builder) {
this.provideIntAProvider = AppModule_ProvideIntAFactory.create(builder.appModule);
this.provideIntBProvider = AppModule_ProvideIntBFactory.create(builder.appModule);
this.provideClassAProvider =
DoubleCheck.provider(
AppModule_ProvideClassAFactory.create(
builder.appModule, provideIntAProvider, provideIntBProvider));
this.myAppMembersInjector = MyApp_MembersInjector.create(provideClassAProvider);
this.activityComponentBuilderProvider =
new dagger.internal.Factory<ActivityComponent.Builder>() {
@Override
public ActivityComponent.Builder get() {
return new ActivityComponentBuilder();
}
};
this.getActivityBuilderProvider = (Provider) activityComponentBuilderProvider;
this.mapOfClassOfAndProviderOfSubcomponentBuilderProvider =
MapProviderFactory.<Class<?>, SubcomponentBuilder>builder(1)
.put(ActivityComponent.Builder.class, getActivityBuilderProvider)
.build();
}
@Override
public void inject(MyApp myApp) {
myAppMembersInjector.injectMembers(myApp);
}
@Override
public Map<Class<?>, Provider<SubcomponentBuilder>> subcomponentBuilders() {
return mapOfClassOfAndProviderOfSubcomponentBuilderProvider.get();
}
public static final class Builder {
private AppModule appModule;
private Builder() {}
public AppComponent build() {
if (appModule == null) {
throw new IllegalStateException(AppModule.class.getCanonicalName() + " must be set");
}
return new DaggerAppComponent(this);
}
public Builder appModule(AppModule appModule) {
this.appModule = Preconditions.checkNotNull(appModule);
return this;
}
}
private final class ActivityComponentBuilder implements ActivityComponent.Builder {
private ActivityModule activityModule;
@Override
public ActivityComponent build() {
if (activityModule == null) {
throw new IllegalStateException(ActivityModule.class.getCanonicalName() + " must be set");
}
return new ActivityComponentImpl(this);
}
@Override
public ActivityComponentBuilder withActivityModule(ActivityModule activityModule) {
this.activityModule = Preconditions.checkNotNull(activityModule);
return this;
}
}
private final class ActivityComponentImpl implements ActivityComponent {
private Provider<Integer> provideCProvider;
private Provider<ClassB> provideClassBProvider;
private MembersInjector<MainActivity> mainActivityMembersInjector;
private ActivityComponentImpl(ActivityComponentBuilder builder) {
assert builder != null;
initialize(builder);
}
@SuppressWarnings("unchecked")
private void initialize(final ActivityComponentBuilder builder) {
this.provideCProvider = ActivityModule_ProvideCFactory.create(builder.activityModule);
this.provideClassBProvider =
DoubleCheck.provider(
ActivityModule_ProvideClassBFactory.create(
builder.activityModule,
DaggerAppComponent.this.provideClassAProvider,
provideCProvider));
this.mainActivityMembersInjector = MainActivity_MembersInjector.create(provideClassBProvider);
}
@Override
public void inject(MainActivity mainActivity) {
mainActivityMembersInjector.injectMembers(mainActivity);
}
}
}
首先有些新的东西是下游的Builder
的实现细节,很典型的建造者模式,这里就不过多分析了,主要是探究Map是怎么生成的。
注意到
@Override
public Map<Class<?>, Provider<SubcomponentBuilder>> subcomponentBuilders() {
return mapOfClassOfAndProviderOfSubcomponentBuilderProvider.get();
}
这个Map同样也被Provider
接口封装,具体在
this.mapOfClassOfAndProviderOfSubcomponentBuilderProvider =
MapProviderFactory.<Class<?>, SubcomponentBuilder>builder(1)
.put(ActivityComponent.Builder.class, getActivityBuilderProvider)
.build();
这里进行初始化。MapProviderFactory
实际上就是一个Map的工厂,把键值对放进去,然后使用get()
来获取Map:
/**
* A {@link Factory} implementation used to implement {@link Map} bindings. This factory returns a
* {@code Map<K, Provider<V>>} when calling {@link #get} (as specified by {@link Factory}).
细节省略……
那么.put(ActivityComponent.Builder.class, getActivityBuilderProvider)
就是放键值对,getActivityBuilderProvider
又从哪里来?this.getActivityBuilderProvider = (Provider) activityComponentBuilderProvider;
然后
this.activityComponentBuilderProvider =
new dagger.internal.Factory<ActivityComponent.Builder>() {
@Override
public ActivityComponent.Builder get() {
return new ActivityComponentBuilder();
}
};
如此就把一个初始化好的ActivityComponentBuilder
插入到了Map中。
之后发生的就很自然了。
总而言之,这种方法复杂度比第一种高得很明显。少写了一行ActivityComponent
的工厂函数,代价是多了一个ActivityBinders
以及配套的接口/注解,同时AppComponent
还得添加其他东西。至于好处那就见仁见智了。