上一期介紹了@Scope
的含義和用法符衔,明白了Dagger2只認這么一個標注残炮,而且認為標注的Component
為單例韭赘。那么假如我們想要實現(xiàn)真正的@PerActivity
,即Activity
范圍內(nèi)為單例势就,或者是其余的自定義范圍標注泉瞻,要如何做到呢?
范圍標注也沒有魔法苞冯,換言之不是標了@PerActivity
它就自動實現(xiàn)Activity
內(nèi)單例袖牙。更確切地講,是因為我們希望Activity
內(nèi)單例舅锄,而且Dagger也會幫我們實現(xiàn)Activity
內(nèi)單例鞭达,才給它標注@PerActivity
。
這里就不得不提Component dependency
與Subcomponent
。它們是不同的東西畴蹭,但功能非常類似坦仍,都能使Component
“層次化”∵督螅“層次化”的含義是繁扎,Component
出現(xiàn)上下級之分,下級Component
依賴于上級Component
的參數(shù)與提供函數(shù)糊闽,然而又可以使用不同的范圍標注梳玫。(事實上,Dagger要求下級Component
必須使用不同的范圍標注右犹,道理也很簡單提澎,相同范圍內(nèi)上級Component
功能完全被下級覆蓋,沒有存在的必要性念链。)
由于篇幅所限盼忌,本期只介紹Component dependency
方法。
所謂Component dependency
就是在上級Component
內(nèi)創(chuàng)建提供函數(shù)掂墓,讓下級Component
通過標注的形式來依賴上級Component
(如果還有下下級碴犬,需要上級的參數(shù),還可繼承上級的接口Component
梆暮,這樣下下級也可以訪問到上級的提供函數(shù))服协。
如此一來,我們可以讓Activity
擁有自己的專屬Component
即ActivityComponent
啦粹,然后每次onCreate
的時候從上級AppComponent
里面造一個下級ActivityComponent
偿荷,這樣還是只需要在初始化上級``AppComponent時提供一次構(gòu)造參數(shù)(當然如果下級
ActivityComponent加入了新的
Module的話還是要提供新參數(shù)),同時又因為下級
ActivityComponent是獨立的唠椭,如果只在此
Activity里面使用跳纳,就真正實現(xiàn)了
@PerActivity`。
接下來我們將:創(chuàng)建AppComponent
贪嫂,@PerApplication
標注寺庄,負責注入ClassA
;
創(chuàng)建ActivityComponent
力崇,@PerActivity
標注斗塘,負責注入ClassB
。ActivityComponent
依賴于AppComponent
亮靴。
共同代碼
無論如何馍盟,先定義好兩個范圍標注:
@Scope
@Documented
@Retention(RUNTIME)
public @interface PerApplication {}
@Scope
@Documented
@Retention(RUNTIME)
public @interface PerActivity {}
然后是ClassA
與ClassB
的代碼,都在構(gòu)造器內(nèi)添加Log以幫助了解構(gòu)造器的調(diào)用:
public class ClassA {
private int a;
private int b;
private static final String TAG = "ClassA";
public ClassA(int a, int b) {
this.a = a;
this.b = b;
Log.d(TAG, "classA constructor called");
}
public int getA() {
return a;
}
public int getB() {
return b;
}
}
public class ClassB {
private static final String TAG = "ClassB";
private ClassA classA;
private int a;
public ClassB(ClassA classA, int a) {
this.classA = classA;
this.a = a;
Log.d(TAG, "classB constructor called");
}
public ClassA getClassA() {
return classA;
}
public int getA() {
return a;
}
}
再把之前關(guān)于ClassBComponent
與ModuleB
的代碼刪掉茧吊。注意運行前rebuild
一下贞岭,避免之前生成的代碼造成影響八毯。
Component Dependency
主體代碼
使用這種方式,上級Component
可以暴露自己的組件給下級Component
瞄桨,方法就是創(chuàng)建一個返回想要暴露的類型的函數(shù)话速,名字還是無所謂。比如這里的AppComponent
是這樣:
@PerApplication
@Component(modules = AppModule.class)
public interface AppComponent {
void inject(MyApp myApp);
ClassA getClassA();
}
對應(yīng)的AppModule
:
@Module
public class AppModule {
private int a;
private int b;
public AppModule(int a, int b) {
this.a = a;
this.b = b;
}
@Provides
@Named("a")
int provideIntA() {
return a;
}
@Provides
@Named("b")
int provideIntB() {
return b;
}
@PerApplication
@Provides
ClassA provideClassA(@Named("a") int a, @Named("b") int b) {
return new ClassA(a, b);
}
}
注意在AppComponent
里面我們通過加入ClassA getClassA();
暴露出ClassA
的@Provides
函數(shù)給其下級芯侥,這樣下級就可以從上級獲取ClassA
而不用自己再寫一遍尿孔。
然后就是ActivityComponent
:
@PerActivity
@Component(dependencies = AppComponent.class, modules = ActivityModule.class)
public interface ActivityComponent {
void inject(MainActivity mainActivity);
}
注意這種聲明依賴的形式。
有時候我們會看到有些項目中筹麸,下級Component
不僅在標注上聲明了dependencies
,還繼承了上級Component
的接口雏婶。其實接口繼承就是單純地繼承上級中的暴露函數(shù)物赶,往往這個Component
還有下級。
如果是最下級的Component
留晚,并沒有理由這么做酵紫。
然后是ActivityModule
:
@Module
public class ActivityModule {
private int c;
public ActivityModule(int c) {
this.c = c;
}
@Provides
public int provideC() {
return this.c;
}
@PerActivity
@Provides
public ClassB provideClassB(ClassA classA, int c) {
return new ClassB(classA, c);
}
}
然后就是MyApp
代碼:
public class MyApp extends Application {
private static MyApp appInstance = null;
private static AppComponent appComponent = null;
@Inject ClassA classa;
@Override
public void onCreate() {
super.onCreate();
appInstance = this;
appComponent = DaggerAppComponent.builder().appModule(new AppModule(2, 3)).build();
appComponent.inject(this);
}
public static MyApp getAppInstance() {
return appInstance;
}
public static AppComponent getAppComponent() {
return appComponent;
}
}
MainActivity
代碼:
public class MainActivity extends AppCompatActivity {
@Inject ClassB classB;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerActivityComponent.builder()
.activityModule(new ActivityModule(4))
.appComponent(MyApp.getAppComponent())
.build().inject(this);
}
}
重復一遍:刪除之前ClassBComponent
與ModuleB
的代碼,rebuild
再運行错维。運行發(fā)現(xiàn)兩個Log分別被打印一次奖地。這說明了A類和B類都只實例化了一次,也就是說B類的注入確實依賴了之前注入的A類赋焕。
生成代碼
現(xiàn)在有5個工廠類参歹,AppModule_ProvideIntAFactory
,AppModule_ProvideIntBFactory
隆判,AppModule_ProvideClassAFactory
犬庇,ActivityModule_ProvideCFactory
與ActivityModule_ProvideClassBFactory
。
兩個注入器MyApp_MembersInjector
與MainActivity_MembersInjector
侨嘀。這些代碼都和之前的非常雷同臭挽。
我們還是把更多精力投入在兩個關(guān)鍵類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 ClassA getClassA() {
return provideClassAProvider.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;
}
}
}
這個類就本身而言和上一期的差距不是很大,可以看出還是用了DoubleCheck
封裝咬腕,說明確實Dagger把@PerApplication
也看做單例欢峰。
唯一的區(qū)別在于,多了getClassA()
這個函數(shù)涨共。這是自然的纽帖,因為這個類說到底是我們定義的AppComponent
接口的實現(xiàn)類,自然要實現(xiàn)里面的抽象方法举反。
接下來這個就更值得仔細研究了:
public final class DaggerActivityComponent implements ActivityComponent {
private Provider<ClassA> getClassAProvider;
private Provider<Integer> provideCProvider;
private Provider<ClassB> provideClassBProvider;
private MembersInjector<MainActivity> mainActivityMembersInjector;
private DaggerActivityComponent(Builder builder) {
assert builder != null;
initialize(builder);
}
public static Builder builder() {
return new Builder();
}
@SuppressWarnings("unchecked")
private void initialize(final Builder builder) {
this.getClassAProvider =
new daggerplay_com_daggerplay_components_AppComponent_getClassA(builder.appComponent);
this.provideCProvider = ActivityModule_ProvideCFactory.create(builder.activityModule);
this.provideClassBProvider =
DoubleCheck.provider(
ActivityModule_ProvideClassBFactory.create(
builder.activityModule, getClassAProvider, provideCProvider));
this.mainActivityMembersInjector = MainActivity_MembersInjector.create(provideClassBProvider);
}
@Override
public void inject(MainActivity mainActivity) {
mainActivityMembersInjector.injectMembers(mainActivity);
}
public static final class Builder {
private ActivityModule activityModule;
private AppComponent appComponent;
private Builder() {}
public ActivityComponent build() {
if (activityModule == null) {
throw new IllegalStateException(ActivityModule.class.getCanonicalName() + " must be set");
}
if (appComponent == null) {
throw new IllegalStateException(AppComponent.class.getCanonicalName() + " must be set");
}
return new DaggerActivityComponent(this);
}
public Builder activityModule(ActivityModule activityModule) {
this.activityModule = Preconditions.checkNotNull(activityModule);
return this;
}
public Builder appComponent(AppComponent appComponent) {
this.appComponent = Preconditions.checkNotNull(appComponent);
return this;
}
}
private static class daggerplay_com_daggerplay_components_AppComponent_getClassA
implements Provider<ClassA> {
private final AppComponent appComponent;
daggerplay_com_daggerplay_components_AppComponent_getClassA(AppComponent appComponent) {
this.appComponent = appComponent;
}
@Override
public ClassA get() {
return Preconditions.checkNotNull(
appComponent.getClassA(), "Cannot return null from a non-@Nullable component method");
}
}
}
這個類創(chuàng)造了一個新的內(nèi)部類抛计,這里是daggerplay_com_daggerplay_components_AppComponent_getClassA
。名字倒是其次照筑,其作用就是接受AppComponent
的實例吹截,然后封裝成為ClassA
的工廠瘦陈。我們在MainActivity
里面?zhèn)魅氲?code>AppComponent的實例正是被如此轉(zhuǎn)化成為了依賴工廠來提供ClassA
實例。另外由于AppComponent
也使用了@PerActivity
標注波俄,注入時還是使用上一期介紹的單例注入方法晨逝,有興趣的同學可以自己動手嘗試。
至此懦铺,我們明白了如果根據(jù)環(huán)境的結(jié)構(gòu)層次來對Dagger2進行分級以及拓展捉貌,不用擔心AppComponent
里面注入太多Application層并用不到的東西,也不用擔心在寫Activity的時候還要把Application里面的東西都再重復一遍了冬念,是不是很有用呢趁窃?
下期繼續(xù)介紹Subcomponent
的使用。