寫在前面
提高篇的目的是想著寫一些較深入的知識闰集,包含一些源碼分析沽讹、架構(gòu)設(shè)想、腳手架搭建的東西武鲁。
面向的人群是中高級的開發(fā)者或者愿意深入了解如何快速構(gòu)建Kotlin&&MVVM應(yīng)用的人群爽雄。
Dagger-Android
原本的打算是將其作為使用Kotlin構(gòu)建MVVM應(yīng)用程序系列的第五部分內(nèi)容。
但因?yàn)镈agger本身就有一定的入門門檻沐鼠,Dagger-Android的門檻就更高了挚瘟。對于初中級開發(fā)者而言,Dagger-Android太容易入門到放棄饲梭,對于這部分人群不是很適合乘盖,因此將其放入提高篇較為合適。
又因?yàn)镈agger-Android門檻較高憔涉,對于初學(xué)者來說不適用订框,就如同初出江湖的小菜鳥就想著去練習(xí)遠(yuǎn)高于自己根基的武功,對自己沒有太多好處兜叨,多積累基礎(chǔ)穿扳,設(shè)計模式才是正途,能力到了自然就悟了国旷。
又由于曲高和寡矛物,懂的人自然懂。為此跪但,我開通了使用Kotlin構(gòu)建MVVM應(yīng)用程序的小專欄履羞,提高篇的完整內(nèi)容會放在這里,愿意去了解的了解一哈屡久。
而這里也就大致的總結(jié)一下它的思路忆首。
為什么要有Dagger-Android?
對于這個問題,google在Dagger-Android的文檔上有解釋:
我們普通的dagger代碼如下所示:
public class FrombulationActivity extends Activity {
@Inject Frombulator frombulator;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// DO THIS FIRST. Otherwise frombulator might be null!
((SomeApplicationBaseType) getContext().getApplicationContext())
.getApplicationComponent()
.newActivityComponentBuilder()
.activity(this)
.build()
.inject(this);
// ... now you can write the exciting code
}
}
這會帶來一些問題:
- 只是復(fù)制粘貼上面的代碼會讓以后的重構(gòu)比較困難被环,還會讓一些開發(fā)者不知道Dagger到底是如何進(jìn)行注入的(然后就更不易理解了)
- 更重要的原因是:它要求注射類型(FrombulationActivity)知道其注射器糙及。 即使這是通過接口而不是具體類型完成的,它打破了依賴注入的核心原則:一個類不應(yīng)該知道如何實(shí)現(xiàn)依賴注入蛤售。
為了解決上述的問題妒潭,Dagger-Android應(yīng)運(yùn)而生揣钦。
這是它的起因冯凹,那么和傳統(tǒng)的Dagger區(qū)別又在哪里呢宇姚?
區(qū)別在哪?
就在解法不同罷了阱持,如果你認(rèn)為Dagger-Android是普通Dagger的延伸衷咽,要按照Dagger的邏輯去理解Dagger-Android镶骗,那么一開始就錯了躲雅。
假設(shè)把依賴注入看成是一道算法題或者數(shù)學(xué)的最后一道大題相赁。
Dagger和Dagger-Android都是正確的答案,只是兩個不同的解法而已裆赵。
相比之下跺嗽,Dagger-Android在程序啟動的時候通過處理注解页藻,初始化了一個全局的單例map
Map<Class<? extends T>, Provider<AndroidInjector.Factory<? extends T>>>
key值為activity/fragment的class桨嫁,而value為提供相應(yīng)Component的Provider對象璃吧。在開始的時候就為我們映射好了废境。
當(dāng)我們調(diào)用AndroidInjection.inject(this)
時,也就是一個簡單的map.get(instance.class).getComponent().inject(this)
相比之下,多了一個從map去獲取component的中間過程毡咏,但是卻解決了上訴的第二個問題 :一個類不應(yīng)該知道如何實(shí)現(xiàn)依賴注入呕缭。
思路的差別就在這里修己,明白這一點(diǎn)睬愤,通過斷點(diǎn)一步步的源碼分析尤辱,Dagger-Android也沒有多么高不可攀啥刻。
快速開始
首先我們需要在app/build.gradle加入相應(yīng)的依賴
//dagger2 di
implementation 'com.google.dagger:dagger:2.16'
kapt 'com.google.dagger:dagger-compiler:2.16'
//dagger-android
implementation 'com.google.dagger:dagger-android:2.16'
implementation 'com.google.dagger:dagger-android-support:2.16'
kapt 'com.google.dagger:dagger-android-processor:2.16'
注入方法建議看文檔更好可帽,這里簡單描述一下:
還是以MVVM-Android為例映跟。
- 添加ActivityModule.kt
@Module
abstract class ActivityModule {
@ContributesAndroidInjector
abstract fun contributePaoActivity(): PaoActivity
}
2 . 修改AppComponent.kt
@Singleton
@Component(modules = arrayOf(
AndroidInjectionModule::class,
AppModule::class,
ActivityModule::class)
)
interface AppComponent {
@Component.Builder
interface Builder {
@BindsInstance
fun application(application: Application): Builder
fun build(): AppComponent
}
fun inject(application: PaoApp)
}
相比Dagger2努隙,modules多了AndroidInjectionModule和ActivityModule兩個類荸镊。
- rebuild一下項(xiàng)目,然后新增PaoApp.kt同時實(shí)現(xiàn)HasActivityInjector接口
class PaoApp : Application(),HasActivityInjector{
@Inject
lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Activity>
override fun onCreate() {
super.onCreate()
DaggerAppComponent.builder().application(app).build().inject(app)
}
override fun activityInjector() = dispatchingAndroidInjector
}
- 最后在Activity的onCreate()方法之前調(diào)用
AndroidInjection.inject(this)
進(jìn)行注入
class PaoActivity : RxAppCompatActivity() {
@Inject
lateinit var mViewModel : PaoViewModel
override fun onCreate(savedInstanceState: Bundle?) {
//////di
AndroidInjection.inject(this)
super.onCreate(savedInstanceState)
}
}
到此张惹,一個簡單的依賴注入就好了宛逗。
當(dāng)然簡單是絕不簡單的雷激。這些是在寫什么?完全云里霧里进栽。剛從Dagger轉(zhuǎn)換成Dagger-Android的直接就勸退了快毛,還不如直接使用Dagger2的好唠帝。
確實(shí)襟衰,對于日常功能迭代的開發(fā)團(tuán)隊(duì)來說瀑晒,普通的dagger更易理解徘意,所以Dagger-Android也算是一個可選項(xiàng)椎咧,可以作為一個提高勤讽,而且google的很多示例里dagger的用法都是Dagger-Android,所以還是有必要懂它的原理向臀。
原理剖析
在第四部分中券膀,我們也了解了普通的Dagger是如何進(jìn)行依賴注入的三娩,這里我們再來回顧一次
由AppModule提供所需的依賴
由AppCompent提供注入的途徑
由@Inject標(biāo)識需要注入的對象
調(diào)用
DaggerAppComponent.builder()
.appModule(AppModule(applicationContext)).build()
.inject(this)
完成依賴注入
這里的邏輯比較好理解一些妹懒,就是普通的
paoActivity.mViewModel = appComponent.paoViewModel
那Dagger-Android相比之下眨唬,又是怎么樣的邏輯呢匾竿?
相比之前岭妖,Dagger-Android將Activity/Fragment所需的compoent都放到了一個map對象里,這個map對象由App的dispatchingAndroidInjector
對象持有假夺。其中key
值為activity/frament的class已卷,value
為提供相應(yīng)Component的Provider對象侧蘸。
Map<Class<? extends T>, Provider<AndroidInjector.Factory<? extends T>>>
當(dāng)我們在Activity中調(diào)用 AndroidInjection.inject(this)
時讳癌,又在做什么呢?
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()));
}
//找到dispatchingAndroidInjector對象
AndroidInjector<Activity> activityInjector =
((HasActivityInjector) application).activityInjector();
checkNotNull(activityInjector, "%s.activityInjector() returned null", application.getClass());
//進(jìn)行注入
activityInjector.inject(activity);
}
而activityInjector.inject(activity)
里面的邏輯又可以描述為
一個全局的單例map對象析桥,通過key值為activity.class即
map.get(activity.class) 找到activity與之對應(yīng)的Provider<AndroidInjector.Factory<? extends T>>
泡仗。猜憎。截亦。
柬讨。踩官。颖系。
然后經(jīng)過層層的深入
找到對應(yīng)的component
最后依然還是調(diào)用activity.viewmodel = component.viewmodel
和普通的dagger對比一下區(qū)別就在于:
Dagger-Android在剛開始的時候通過注解處理器分析@Component、@Module信粮、@ContributesAndroidInjector 等等注解强缘,幫我們在App啟動的時候建立了一個全局的單例map欺旧,并添加相關(guān)的映射辞友。
可以在生成的DaggerAppComponet.kt
文件中找到
private Map<Class<? extends Activity>, Provider<AndroidInjector.Factory<? extends Activity>>>
getMapOfClassOfAndProviderOfFactoryOf() {
return Collections
.<Class<? extends Activity>, Provider<AndroidInjector.Factory<? extends Activity>>>
singletonMap(PaoActivity.class, (Provider) paoActivitySubcomponentBuilderProvider);
}
當(dāng)注入的時候就間接的通過這個map找到對應(yīng)activity需要的Component,完成注入鲫尊。
接下來我們就具體來看看 activityInjector.inject(activity)
是如何完成注入的沦偎。
注入過程
先來斷點(diǎn)調(diào)試一下豪嚎,看看activityInjector是什么侈询?
)
可以看到activityInjector
的真身是DispatchingAndroidInjector
扔字,實(shí)際調(diào)用的是DispatchingAndroidInjector
的inject()方法囊嘉,接著看看inject()
方法
調(diào)用的是maybeInject(instance)
,繼續(xù)深入
到這里就很清晰了革为,正如前文所說的那樣扭粱,通過一個單例map根據(jù)key值為instance.class
找到相應(yīng)的factoryProvider,通過get()
方法獲取到AndroidInjector.Factory<T>
對象
而它的真身是DaggerAppComponent
的內(nèi)部類PaoActivitySubcomponentBuilder
震檩。
private final class PaoActivitySubcomponentBuilder
extends ActivityModule_ContributePaoActivity.PaoActivitySubcomponent.Builder {
//....
}
PaoActivitySubcomponentBuilder 繼承了ActivityModule_ContributePaoActivity.PaoActivitySubcomponent.Builder琢蛤,再來看看ActivityModule_ContributePaoActivity
@Module(subcomponents = ActivityModule_ContributePaoActivity.PaoActivitySubcomponent.class)
public abstract class ActivityModule_ContributePaoActivity {
private ActivityModule_ContributePaoActivity() {}
@Binds
@IntoMap
@ActivityKey(PaoActivity.class)
abstract AndroidInjector.Factory<? extends Activity> bindAndroidInjectorFactory(
PaoActivitySubcomponent.Builder builder);
@Subcomponent
public interface PaoActivitySubcomponent extends AndroidInjector<PaoActivity> {
@Subcomponent.Builder
abstract class Builder extends AndroidInjector.Builder<PaoActivity> {}
}
}
這些代碼是添加映射的關(guān)鍵代碼了,Dagger-Android通過處理@ContributesAndroidInjector
自動生成的,按照Dagger-Android文檔上的說法是可以自己編寫虐块,@ContributesAndroidInjector
只是簡化了這步操作嘉蕾。
可以看到@Binds儡率、@IntoMap、@ActivityKey 這幾個注解,就如前文所說的那樣將其保存到單例的map對象之中浪汪,key值便是PaoActivity.class
凯旋。
這些都是題外話钠署,再回頭繼續(xù)斷點(diǎn)調(diào)試,可以看到factory.create(instance)
。
調(diào)用了seedInstance()
方法傍菇,這是一個抽象方法淮悼,由前文的PaoActivitySubcomponentBuilder
實(shí)現(xiàn)。
private final class PaoActivitySubcomponentBuilder
extends ActivityModule_ContributePaoActivity.PaoActivitySubcomponent.Builder {
private PaoActivity seedInstance;
@Override
public void seedInstance(PaoActivity arg0) {
this.seedInstance = Preconditions.checkNotNull(arg0);
}
}
就是一個簡單的賦值操作损痰。然后返回類型為AndroidInjector<T>
的injector
肪凛,斷點(diǎn)可以看到它的真身是PaoActivitySubcomponentImpl
爹袁。
繼續(xù)往下看,來到 injector.inject(instance);
到了這一步,就跟以前的Dagger沒任何區(qū)別了浦妄。
private final class PaoActivitySubcomponentImpl
implements ActivityModule_ContributePaoActivity.PaoActivitySubcomponent {
private PaoActivitySubcomponentImpl(PaoActivitySubcomponentBuilder builder) {}
private PaoRepo getPaoRepo() {
return new PaoRepo(
DaggerAppComponent.this.providePaoServiceProvider.get(),
DaggerAppComponent.this.providePaoDaoProvider.get());
}
private PaoViewModel getPaoViewModel() {
return new PaoViewModel(getPaoRepo());
}
@Override
public void inject(PaoActivity arg0) {
injectPaoActivity(arg0);
}
private PaoActivity injectPaoActivity(PaoActivity instance) {
PaoActivity_MembersInjector.injectMViewModel(instance, getPaoViewModel());
return instance;
}
}
}
到此,經(jīng)過分析Dagger-Android的注入過程耳胎,我們了解了他們的工作原理。
最后總結(jié)歸納一下:
- 普通的賦值:viewmodel = ViewModel(Repo())
- Dagger的注入: instance.viewmodel = component.viewmodel
- Dagger-Android的注入:instance.viewmodel = map.get(instance.class).getComponent().viewmodel
就一個思路的轉(zhuǎn)變,google的大神真是有心了搔体,搞這么多事劝术。
寫在最后
Dagger-Android相比于普通的Dagger確實(shí)稍微繞了一些绳泉,多了一些設(shè)計模式和面向接口,光看源碼的話很容易繞暈,特別是在不懂得google大神們的思路的時候月腋。
如果說Dagger的復(fù)雜度是5,那么Dagger-Android的復(fù)雜程度就是7。
如果能明悟的話所禀,邏輯也是很簡單的操禀,然后多進(jìn)行斷點(diǎn)調(diào)試。就像解算法題一樣,Dagger和Dagger-Android可以算是兩種思路吧溪猿。
如同寫在前面的話里提到的,Dagger適合于那些初中級開發(fā)者的團(tuán)隊(duì)纫塌,比較容易理解诊县。Dagger-Android則更適合實(shí)力都較強(qiáng)的開發(fā)團(tuán)隊(duì)。)
github:https://github.com/ditclear/MVVM-Android/tree/dagger-android
參考文檔:
Dagger-Android
告別Dagger2模板代碼:DaggerAndroid原理解析 (推薦)