近期接手了公司一個(gè)項(xiàng)目的重構(gòu),要基于MVPArms框架來做,而arms又是基于dagger2構(gòu)建;但是dagger2直接用于android的話用起來還是有些不太舒服,仗著自己對(duì)dagger有些了解,花了些時(shí)間把a(bǔ)rms框架的dagger部分替換成了dagger-android。重構(gòu)期間也遇到了一些問題,修修補(bǔ)補(bǔ)最后還算成功,對(duì)于dagger的理解也更深了一點(diǎn);為了幫助自己記憶開始寫這篇文章,同時(shí)也在這里分享給大家。
關(guān)于dagger2網(wǎng)上已經(jīng)有了不少優(yōu)秀的文章,我這里就直接從dagger-android開始講起了,如果還對(duì)dagger2不太了解,建議先看看這個(gè)系列的文章Dagger2完全解析,個(gè)人認(rèn)為非常不錯(cuò)。
1. 一些概念
首先我們來回顧一下dagger三要素
目標(biāo)類: 需要進(jìn)行依賴注入的類,即依賴于一些其他類實(shí)例的類。
module: 準(zhǔn)確地說應(yīng)該是依賴,用于為目標(biāo)類提供依賴。我們可以通過創(chuàng)建一個(gè)module手動(dòng)new對(duì)象對(duì)外提供依賴,也可以通過@inject注解類的構(gòu)造器讓dagger自動(dòng)創(chuàng)建對(duì)象提供依賴。
-
commponent: 翻譯過來是組件的意思,在dagger中充當(dāng)ioc容器。我們知道component可以依賴一個(gè)或多個(gè)module,然后它一般還需要有一個(gè)inject方法,類似這樣:
@Component(modules = { xxxModule.class, xxxModule.class }) public interface AppComponent { void inject(App app); }那么它的作用就顯而易見了——component作為一個(gè)容器,封裝了它依賴的所有module提供的創(chuàng)建對(duì)象的方式。經(jīng)過dagger的編譯以后,我們?cè)谀繕?biāo)類調(diào)用component實(shí)現(xiàn)類的inject方法將它提供的依賴注入進(jìn)來,之后就可以通過@inject注解從component里面拿到我們所需的對(duì)象了。
2. 如何構(gòu)建依賴關(guān)系
我們知道一個(gè)android項(xiàng)目必定有application,還可能有activity、fragment、service...這些組件,通常我們會(huì)在application(或者單例類)里面保存一些全局變量,以便在其他地方使用;而在activity中也可能需要暴露出共有變量給屬于它的fragments使用(比如在有多個(gè)fragment的tab頁面中),這樣的話我們不得不通過get或者setArguments方法傳遞這些對(duì)象,非常地不優(yōu)雅。下面我們來看看dagger的方式。
2.1 回顧dagger的構(gòu)建方式
現(xiàn)在我們從dagger的角度來看,我們可以把整個(gè)app看作一個(gè)根組件,創(chuàng)建一個(gè)appComponent;activity看作它的子組件,然后依次創(chuàng)建xxxActivityComponent,將它聲明為appComponent的子組件;
@Component(modules = {ActivityModule.class, ...})
public interface AppComponent {...}
@Module(subcomponents = {MainComponent.class, ...})
public abstract class ActivityModule {...}
@ActivityScope
@Subcomponent
public interface MainComponent {
void inject(MainActivity activity);
@Subcomponent.Builder
interface Builder {
@BindsInstance
Builder view(MainContract.View view);
MainComponent build();
}
}
然后在每個(gè)activity里面注入它自己的component
App.get(this)
.getAppComponent()
.mainComponentBuilder()
.view(this)
.build()
.inject(this);
@BindsInstance: 由于項(xiàng)目用的是MVP模式,需要給presenter提供view實(shí)例,這里的view實(shí)際上就是activity,它只能通過我們?cè)谧⑷氲臅r(shí)候傳入(我們無法實(shí)例化activity),而@BindsInstance注解的view方法就是把a(bǔ)ctivity作為view實(shí)例放到容器里了。
以上這些看起來似乎沒什么問題,但是要知道我們一個(gè)項(xiàng)目里可能很多個(gè)activity,每個(gè)都需要這樣去注入的話是非常麻煩的,而且這種注入方式看起來并不是一目了然。
下面我們來看看dagger.android的注入方式。
2.2 dagger.android的構(gòu)建方式
首先build.gradle中加入依賴包
implementation "com.google.dagger:dagger:$version_dagger2"
annotationProcessor "com.google.dagger:dagger-compiler:$version_dagger2"
implementation "com.google.dagger:dagger-android:$version_dagger2"
implementation "com.google.dagger:dagger-android-support:$version_dagger2"
annotationProcessor "com.google.dagger:dagger-android-processor:$version_dagger2"
接下來直接看看最終如何在目標(biāo)類中的注入依賴
AndroidInjection.inject(四大組件或者fragment)
AndroidSupportInjection.inject(v4包下的fragment)
注意,需要BroadcastReceiver中super.onReceive()之前調(diào)用, fragment super.onAttach()之前,其他三大組件super.onCreate()之前調(diào)用。
如此簡(jiǎn)單!不再需要顯式地在目標(biāo)類中指明要注入哪個(gè)component,我們甚至能把它放到基類中就可以完成注入。這是怎么做到的呢?我們待會(huì)再來細(xì)說,再此之前我們還需要做一些別的事情。
-
AppComponent需要做一些調(diào)整
@Component(modules = { AndroidInjectionModule.class, AndroidSupportInjectionModule.class, ActivityModule.class }) public interface AppComponent {...}AndroidInjectionModule, AndroidSupportInjectionModule分別是為了保證四大組件和fragment能夠被注入到容器中,后者是為了支持v4包下的fragment。
-
聲明activity對(duì)應(yīng)的子component方式需要改變
@ActivityScope @Subcomponent(modules = {MainModule.class}) public interface MainComponent extends AndroidInjector<MainActivity> { @Subcomponent.Builder abstract class Builder extends AndroidInjector.Builder<MainActivity> {} } @Module public abstract class MainModule { @ActivityScope @Binds abstract MainContract.View provideView(MainActivity activity); }子component需要繼承AndroidInjector<四大組件或者fragment>接口,同時(shí)builder也需要繼承AndroidInjector.Builder<四大組件或者fragment>。AndroidInjector是干嗎用的?我們可以把它理解為注入器,有將commponent注入到目標(biāo)類能力的東西,但它只是一個(gè)接口,具體的實(shí)現(xiàn)在子類(就是我們的component的實(shí)現(xiàn)類,由dagger編譯后生成,代碼位于build/source/apt/.../DaggerAppComponent.java)。
你可能發(fā)現(xiàn)我們少了一些東西,我們并沒有像之前那樣把a(bǔ)ctivity作為MVP中的view傳入component,而是直接在module里面直接拿到了activity,那么這個(gè)activity是從哪里來的呢?還記得
AndroidInjection.inject(xxxActivity)嗎,當(dāng)我們?cè)诮oactivity注入component的時(shí)候,同時(shí)也將activity本身傳入進(jìn)去了,所以我們可以直接用它。 -
需要告訴dagger如何創(chuàng)建一個(gè)子component
首先我們創(chuàng)建一個(gè)ActivityModule,用來管理所有activity對(duì)應(yīng)的component@Module(subcomponents = MainComponent.class) public abstract class ActivityModule { @Binds @IntoMap @ActivityKey(MainActivity.class) abstract AndroidInjector.Factory<? extends Activity> bindMainActivity(MainComponent.Builder builder); }這段代碼實(shí)際上是告訴dagger MainActivity所對(duì)應(yīng)的component是哪一個(gè),簡(jiǎn)單點(diǎn)說,就是將activity的具體類型作為key,它對(duì)應(yīng)的component的創(chuàng)建方式(AndroidInjector.Factory<? extends Activity>,也就是component的工廠對(duì)象)作為值存到一個(gè)map里面,然后我們通過
AndroidInjection.inject(xxxActivity)注入時(shí),dagger就能通過傳入的類型去拿到對(duì)應(yīng)的工廠實(shí)例,創(chuàng)建component然后注入進(jìn)來。當(dāng)然,如果我們需要注入的是其他組件,那么@ActivityKey就應(yīng)該換成對(duì)應(yīng)的xxxKey了。 -
在application里面注入根component
public class App extends Application implements HasActivityInjector { @Inject DispatchingAndroidInjector<Activity> actInjector; @Override public void onCreate() { super.onCreate(); DaggerAppComponent.builder() .appModule(new AppModule(this)) .build() .inject(this); } @Override public AndroidInjector<Activity> activityInjector() { return actInjector; } }如果我們要用dagger.android的方式在activity中注入依賴,我們的application就需要實(shí)現(xiàn)HasActivityInjector接口,它只有一個(gè)抽象方法,需要返回一個(gè)AndroidInjector<Activity>的實(shí)例,而AndroidInjector本身是一個(gè)接口。這時(shí)候我們通過@inject注入一個(gè)DispatchingAndroidInjector<Activity>進(jìn)來就好了(如果需要用到其他組件或者fragment,也要實(shí)現(xiàn)對(duì)應(yīng)的HasXXXInjector接口),最后別忘了注入根component。
那么這個(gè)DispatchingAndroidInjector有什么作用?它從哪里來到哪里去呢?我們來看一段它的源碼。
@Beta public final class DispatchingAndroidInjector<T> implements AndroidInjector<T> { ... private final Map<Class<? extends T>, Provider<AndroidInjector.Factory<? extends T>>> injectorFactories; @Inject DispatchingAndroidInjector( Map<Class<? extends T>, Provider<AndroidInjector.Factory<? extends T>>> injectorFactories) { this.injectorFactories = injectorFactories; } }到這里我們應(yīng)該能馬上明白了,它的構(gòu)造方法被@inject注解了,所以可以直接被注入。而它內(nèi)部保存了一個(gè)map,在構(gòu)造器中被注入賦值。其實(shí)這個(gè)map就是之前我們?cè)谏弦还?jié)講過的,保存了每個(gè)activity(或其他組件)對(duì)應(yīng)的component的工廠實(shí)例。這個(gè)map又是從哪來的呢?答案就在AndroidInjectionModule里,還記得嗎,我們?cè)贏ppComponent中安裝了它,現(xiàn)在來看一看它的代碼。
@Beta @Module public abstract class AndroidInjectionModule { @Multibinds abstract Map<Class<? extends Activity>, AndroidInjector.Factory<? extends Activity>> activityInjectorFactories(); @Multibinds abstract Map<Class<? extends Fragment>, AndroidInjector.Factory<? extends Fragment>> fragmentInjectorFactories(); ...... }一目了然,它直接對(duì)AppComponent提供了裝有各種component工廠實(shí)例的map。
@Multibinds這個(gè)注解需要結(jié)合之前創(chuàng)建的ActivityModule來看@Binds @IntoMap @ActivityKey(MainActivity.class) abstract AndroidInjector.Factory<? extends Activity> bindMainActivity(MainComponent.Builder builder);@Binds把builder轉(zhuǎn)化為它的父類AndroidInjector.Factory<T>,然后@IntoMap將它綁定到@Multibinds注解的AndroidInjectionModule中提供Map<T, T>類型依賴的方法,這樣就根據(jù)<T>分類將每個(gè)子component的工廠實(shí)例放入一個(gè)Map<T, T>中,然后作為依賴提供出去。
3. 更優(yōu)雅的方式
寫了寫么多東西,看起來dagger.adroid的方式好像并沒有好到哪里去,只是避免了在每個(gè)目標(biāo)類中寫一長(zhǎng)串的注入代碼,但是別急,我們還有終極大招。
先來看看最終代碼
@Module
public abstract class ActivityModule {
@ActivityScope
@ContributesAndroidInjector(modules = MainModule.class)
abstract MainActivity provideMainActivity();
}
ActivityModule直接簡(jiǎn)化成了這個(gè)樣子,而且我們不再需要MainComponent(不再需要顯式地創(chuàng)建子Component),子component依賴的module現(xiàn)在直接聲明在@ContributesAndroidInjector里面就好了。
@ContributesAndroidInjector 做了什么呢?其實(shí)一切并沒有變,它只是隱式地幫我們創(chuàng)建了一個(gè)子component,看看編譯后的代碼build/source/apt/.../ActivityModule_ProvideMainActivity
@Module(
subcomponents = ActivityModule_ProvideMainActivity.MainActivitySubcomponent.class
)
@Generated("dagger.android.processor.AndroidProcessor")
public abstract class ActivityModule_ProvideMainActivity {
private ActivityModule_ProvideMainActivity() {}
@Binds
@IntoMap
@ActivityKey(MainActivity.class)
abstract AndroidInjector.Factory<? extends Activity> bindAndroidInjectorFactory(
MainActivitySubcomponent.Builder builder);
@Subcomponent(modules = MainModule.class)
@ActivityScope
public interface MainActivitySubcomponent extends AndroidInjector<MainActivity> {
@Subcomponent.Builder
abstract class Builder extends AndroidInjector.Builder<MainActivity> {}
}
}
看吧,一切并沒有變。
4. 從源碼理清思路
知其然不知其所以然往往容易犯錯(cuò),上一部分的結(jié)尾我們分析了DispatchingAndroidInjector的一些東西,現(xiàn)在我們來跟著源碼從頭理一理思路。
還是從AndroidInjection.inject(xxx)開始,我們點(diǎn)進(jìn)源碼
public static void inject(Activity activity) {
checkNotNull(activity, "activity");
Application application = activity.getApplication();
if (!(application instanceof HasActivityInjector)) {
throw new RuntimeException(
String.format(
"%s does not implement %s",
application.getClass().getCanonicalName(),
HasActivityInjector.class.getCanonicalName()));
}
AndroidInjector<Activity> activityInjector =
((HasActivityInjector) application).activityInjector();
checkNotNull(activityInjector, "%s.activityInjector() returned null", application.getClass());
activityInjector.inject(activity);
}
很簡(jiǎn)單,判斷application是否實(shí)現(xiàn)了HasActivityInjector接口,然后調(diào)用application的activityInjector函數(shù)拿到AndroidInjector實(shí)例(正是我們之前在applicaton中@inject進(jìn)去的DispatchingAndroidInjector),最后調(diào)用了DispatchingAndroidInjector的inject方法,我們接著往下面看,回到DispatchingAndroidInjector
@Beta
public final class DispatchingAndroidInjector<T> implements AndroidInjector<T> {
......
@Override
public void inject(T instance) {
boolean wasInjected = maybeInject(instance);
if (!wasInjected) {
throw new IllegalArgumentException(errorMessageSuggestions(instance));
}
}
@CanIgnoreReturnValue
public boolean maybeInject(T instance) {
Provider<AndroidInjector.Factory<? extends T>> factoryProvider =
injectorFactories.get(instance.getClass());
if (factoryProvider == null) {
return false;
}
@SuppressWarnings("unchecked")
AndroidInjector.Factory<T> factory = (AndroidInjector.Factory<T>) factoryProvider.get();
try {
AndroidInjector<T> injector =
checkNotNull(
factory.create(instance), "%s.create(I) should not return null.", factory.getClass());
injector.inject(instance);
return true;
} catch (ClassCastException e) {
throw new InvalidInjectorBindingException(
String.format(
"%s does not implement AndroidInjector.Factory<%s>",
factory.getClass().getCanonicalName(), instance.getClass().getCanonicalName()),
e);
}
}
}
我們直接看最終調(diào)用的maybeInject(),它根據(jù)我們傳入的目標(biāo)類的class從map中拿到對(duì)應(yīng)的component(我們的子component全部實(shí)現(xiàn)了AndroidInjector接口,所以這里的injector就是component)的工廠實(shí)例,然后創(chuàng)建了component,最后調(diào)用它的inject方法將依賴注入到目標(biāo)類的實(shí)例里面。具體的注入過程可以在dagger為我們生成的xxxComponentImpl里面看到,它是DaggerAppComponent的內(nèi)部類。
現(xiàn)在來總結(jié)一下,首先AndroidInjector接口只有一個(gè)inject抽象方法,我們的子component和DispatchingAndroidInjector都是它的子類,當(dāng)通過AndroidInjection.inject(xxx)注入的時(shí)候,實(shí)際上是調(diào)用application里的DispatchingAndroidInjector的inject方法,而它本身并不做實(shí)際的注入,只是起了一個(gè)分發(fā)的作用,最后找到并調(diào)用了當(dāng)前子component(injecttor)的inject方法進(jìn)行實(shí)際的依賴注入。
5. 結(jié)合實(shí)例
demo地址:https://github.com/ColorfulHorse/MVPDemo
demo很簡(jiǎn)單,主要就是一個(gè)mainActivity通過viewPager管理了三個(gè)fragment,weeklyFragment里面請(qǐng)求了一個(gè)七天的天氣接口,列表展示了出來,通過數(shù)據(jù)庫做了本地緩存。
看看AppComponent
@Singleton
@Component(modules = {
AndroidInjectionModule.class,
AppModule.class,
OkhttpModule.class,
RetrofitModule.class,
ApiServiceModule.class,
DBModule.class,
ActivityModule.class})
public interface AppComponent {
void inject(App app);
@Component.Builder
interface Builder {
@BindsInstance
Builder application(Application application);
AppComponent build();
}
}
依賴了若干module,分別提供okhttpClient實(shí)例,retrofit實(shí)例,retrofitService實(shí)例,數(shù)據(jù)庫實(shí)例;這里數(shù)據(jù)庫用的是ObjectBox庫,有興趣的可以了解一下。
其實(shí)也不需要?jiǎng)?chuàng)建這么多module,也可以全部放到一個(gè)appModule里面就可以,我這里只是做了一個(gè)分類,具體情況視項(xiàng)目復(fù)雜度而定。
再看看AcrivityModule
@Module
public abstract class ActivityModule {
@ActivityScope
@ContributesAndroidInjector(modules = {
MainModule.class,
MainFragmentModule.class
})
abstract MainActivity provideMainActivity();
@ContributesAndroidInjector
@ActivityScope
abstract ForecastActivity provideForecastActivity();
}
管理了一個(gè)列表activity和一個(gè)詳情activity, MainActivity有一個(gè)MainFragmentModule管理其下的三個(gè)fragment
@Module
public abstract class MainFragmentModule {
@FragmentScope
@ContributesAndroidInjector
abstract DailyFragment provideDailyFragment();
@FragmentScope
@ContributesAndroidInjector(modules = WeeklyModule.class)
abstract WeeklyFragment provideWeeklyFragment();
@FragmentScope
@ContributesAndroidInjector
abstract MonthlyFragment provideMonthlyFragment();
}
這樣看起來整個(gè)結(jié)構(gòu)就比較明朗,依賴關(guān)系層次明顯,appComponent為根組件,其下activityComponent為子組件,再下面還有fragment對(duì)應(yīng)的子組件。當(dāng)然這一切只是一個(gè)簡(jiǎn)單的示例,也許還有更好的方式等你探索。
本文到這里也就結(jié)束了,更多的東西demo里面都寫得比較完整,就不再細(xì)講。
如有不足之處歡迎指出。