上一期介紹了Component dependency
方法,這一期介紹SubComponent
方法埋涧,效果是類似的闯睹,只是實(shí)現(xiàn)手段有一點(diǎn)不一樣剿干。
SubComponent
的聲明不再是用@Component
標(biāo)識(shí),而是使用@Subcomponent
標(biāo)識(shí)怕磨,不再有dependency
參數(shù)喂饥,同時(shí)上級(jí)Component
也無需再添加暴露函數(shù)。
那么SubComponent
是如何與上級(jí)建立聯(lián)系的呢肠鲫?
有兩種方式:
- 在上級(jí)
Component
內(nèi)添加獲得該SubComponent
的抽象方法员帮; - 從v2.7開始,允許在
SubComponent
內(nèi)添加builder
导饲,使用build()
方法來獲取實(shí)例捞高,同時(shí)還需要在上級(jí)Component
內(nèi)添加SubComponent
的binder
的相關(guān)代碼,來實(shí)現(xiàn)映射渣锦。這種方法壞處是復(fù)雜很多硝岗,好處是解耦?以我目前看來泡挺,雖然不用寫工廠方法了辈讶,可還是要加更多新的東西到上級(jí),代價(jià)實(shí)在不對(duì)等娄猫。不過既然這個(gè)功能在這里贱除,還是了解一下比較好。
第一種方法
主體代碼
和上一期的非常類似媳溺,稍微做一下修改:
AppComponent
不用再暴露ClassA
的方法月幌,而加上ActivityComponent
的構(gòu)造方法:
@PerApplication
@Component(modules = AppModule.class)
public interface AppComponent {
void inject(MyApp myApp);
ActivityComponent getActivityComponent(ActivityModule activityModule);
}
注意和提供/暴露函數(shù)不太一樣,這里雖然ActivityComponent
自己有范圍標(biāo)注悬蔽,但在這里不必標(biāo)出扯躺。你也可以標(biāo)出,但不會(huì)造成任何區(qū)別蝎困。ActivityComponent
不是注入對(duì)象录语,并不會(huì)變成單例模式。
@PerActivity
@Subcomponent(modules = ActivityModule.class)
public interface ActivityComponent {
void inject(MainActivity mainActivity);
}
Module
部分沒有變化禾乘。然后就是使用的時(shí)候有區(qū)別:
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);
}
}
現(xiàn)在ActivityComponent
是由AppComponent
產(chǎn)生澎埠。運(yùn)行之前還是要rebuild
一下。
可以看到始藕,這種方法和上一期的難易程度相差不大蒲稳,不過由于Dagger1的時(shí)代就有類似Component dependency
的模式氮趋,因此可能Component dependency
用的更多一些。
生成代碼
還是來看看DaggerAppComponent
吧〗現(xiàn)在DaggerActivityComponent
已經(jīng)不存在了剩胁。
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);
}
}
}
可見其內(nèi)部新生成了一個(gè)類,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);
}
}
現(xiàn)在AppComponent
沒有工廠方法了,ActivityComponent
需要提供一個(gè)Builder
來用參數(shù)構(gòu)造自己系宫。注意到@Subcomponent.Builder
標(biāo)注索昂,看看究竟:
/**
* 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
接口需要有一個(gè)方法來幫助設(shè)置ActivityModule
扩借,在這里就是withActivityModule
方法椒惨,名字仍舊不重要。
SubcomponentBuilder
是自己定義的一個(gè)接口潮罪,好在如果使用第二種方式康谆,這個(gè)接口可以復(fù)用在其他Subcomponent
里:
public interface SubcomponentBuilder<V> {
V build();
}
名字不重要,重要的是要帶有build()
方法嫉到。這個(gè)接口與Builder
配合來實(shí)現(xiàn)Builder.build()
沃暗。
AppComponent
@PerApplication
@Component(modules = {AppModule.class, ActivityBinders.class})
public interface AppComponent {
void inject(MyApp myApp);
Map<Class<?>, Provider<SubcomponentBuilder>> subcomponentBuilders();
}
首先介紹一下ActivityBinders
,這個(gè)負(fù)責(zé)獲取對(duì)應(yīng)的Builder
:
@Module(subcomponents={ ActivityComponent.class })
public abstract class ActivityBinders {
@Binds
@IntoMap
@SubcomponentKey(Builder.class)
public abstract SubcomponentBuilder getActivityBuilder(Builder impl);
}
注意其標(biāo)注何恶,屬于@Module
而且要聲明對(duì)應(yīng)的Subcomponent
孽锥。
出現(xiàn)了三個(gè)新標(biāo)注。
@Binds
說是新標(biāo)注细层,其實(shí)在其他地方也見過惜辑。
@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 >Map multibinding</a>
*/
@Documented
@Target(METHOD)
@Retention(RUNTIME)
public @interface IntoMap {}
大意是標(biāo)注的方法會(huì)和某個(gè)Map<K, Provider<V>>
建立聯(lián)系,準(zhǔn)確地說是返回類型和標(biāo)注類型會(huì)成為一個(gè)鍵值對(duì)疫赎。
返回類型這里自然是SubcomponentBuilder
的實(shí)現(xiàn)類盛撑,標(biāo)注類型就是下面@SubcomponentKey(Builder.class)
標(biāo)注的Builder.class
,這個(gè)Builder
就是ActivityComponent
里面的Builder
捧搞。
而SubcomponentKey
也是要自己添加的一個(gè)標(biāo)注接口抵卫,好在還是可以復(fù)用。
@MapKey
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface SubcomponentKey {
Class<?> value();
}
其實(shí)總的來說胎撇,現(xiàn)在AppComponent
不直接產(chǎn)生ActivityComponent
介粘,而是生成一個(gè)Map,key
就是Builder
的類(由@SubcomponentKey規(guī)定晚树,當(dāng)然你也可以直接用@MapKey
碗短,不過具體實(shí)現(xiàn)有點(diǎn)不同),value
自然就是對(duì)應(yīng)的Builder
的Provider
封裝的工廠题涨。ActivityComponent
自己查表獲取Builder
再傳入ActivityModule
完成建造偎谁,從而再注入。
當(dāng)然了纲堵,這里使用了一些幫助的擴(kuò)展接口/標(biāo)注巡雨,從而使得添加多個(gè)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
的實(shí)現(xiàn)細(xì)節(jié),很典型的建造者模式茂附,這里就不過多分析了正蛙,主要是探究Map是怎么生成的。
注意到
@Override
public Map<Class<?>, Provider<SubcomponentBuilder>> subcomponentBuilders() {
return mapOfClassOfAndProviderOfSubcomponentBuilderProvider.get();
}
這個(gè)Map同樣也被Provider
接口封裝营曼,具體在
this.mapOfClassOfAndProviderOfSubcomponentBuilderProvider =
MapProviderFactory.<Class<?>, SubcomponentBuilder>builder(1)
.put(ActivityComponent.Builder.class, getActivityBuilderProvider)
.build();
這里進(jìn)行初始化乒验。MapProviderFactory
實(shí)際上就是一個(gè)Map的工廠,把鍵值對(duì)放進(jìn)去蒂阱,然后使用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}).
細(xì)節(jié)省略……
那么.put(ActivityComponent.Builder.class, getActivityBuilderProvider)
就是放鍵值對(duì)锻全,getActivityBuilderProvider
又從哪里來?this.getActivityBuilderProvider = (Provider) activityComponentBuilderProvider;
然后
this.activityComponentBuilderProvider =
new dagger.internal.Factory<ActivityComponent.Builder>() {
@Override
public ActivityComponent.Builder get() {
return new ActivityComponentBuilder();
}
};
如此就把一個(gè)初始化好的ActivityComponentBuilder
插入到了Map中录煤。
之后發(fā)生的就很自然了鳄厌。
總而言之,這種方法復(fù)雜度比第一種高得很明顯妈踊。少寫了一行ActivityComponent
的工廠函數(shù)了嚎,代價(jià)是多了一個(gè)ActivityBinders
以及配套的接口/注解,同時(shí)AppComponent
還得添加其他東西廊营。至于好處那就見仁見智了歪泳。