轉載自:blog.csdn.net/hsk256/arti…html
如今Dagger2在項目裏用的愈來愈多了,最近花了些時間學習了一下Dagger2,這篇文章主要幫助理解Dagger2的注入實現過程,若有錯誤,還請指正!java
Dagger2是Dagger的升級版,是一個依賴注入框架,如今由Google接手維護。 恩,這裏有個關鍵字依賴注入,所以咱們得先知道什麼是依賴注入,才能更好的理解Dagger2。android
依賴注入是面向對象編程的一種設計模式,其目的是爲了下降程序耦合,這個耦合就是類之間的依賴引發的。git
舉個例子:咱們在寫面向對象程序時,每每會用到組合,即在一個類中引用另外一個類,從而能夠調用引用的類的方法完成某些功能,就像下面這樣.github
public class ClassA {
...
ClassB b;
...
public ClassA() {
b = new ClassB();
}
public void do() {
...
b.doSomething();
...
}
}
複製代碼
這個時候就產生了依賴問題,ClassA依賴於ClassB,必須藉助ClassB的方法,才能完成一些功能。這樣看好像並無什麼問題,可是咱們在ClassA的構造方法裏面直接建立了ClassB的實例,問題就出如今這,在ClassA裏直接建立ClassB實例,違背了單一職責原則,ClassB實例的建立不該由ClassA來完成;其次耦合度增長,擴展性差,若是咱們想在實例化ClassB的時候傳入參數,那麼不得不改動ClassA的構造方法,不符合開閉原則。編程
所以咱們須要一種注入方式,將依賴注入到宿主類(或者叫目標類)中,從而解決上面所述的問題。依賴注入有一下幾種方式:設計模式
經過接口注入app
interface ClassBInterface {
void setB(ClassB b);
}
public class ClassA implements ClassBInterface {
ClassB classB;
@override
void setB(ClassB b) {
classB = b;
}
}
複製代碼
經過set方法注入框架
public class ClassA {
ClassB classB;
public void setClassB(ClassB b) {
classB = b;
}
}
複製代碼
經過構造方法注入ide
public class ClassA {
ClassB classB;
public void ClassA(ClassB b) {
classB = b;
}
複製代碼
經過Java註解
public class ClassA {
//此時並不會完成注入,還須要依賴注入框架的支持,如RoboGuice,Dagger2
@inject ClassB classB;
...
public ClassA() {}
複製代碼
在Dagger2中用的就是最後一種注入方式,經過註解的方式,將依賴注入到宿主類中。
配置apt插件(在build.gradle(Project:xxx)中添加以下代碼)
dependencies {
classpath 'com.android.tools.build:gradle:2.1.0'
//添加apt插件
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
}
複製代碼
添加依賴(在build.gradle(Module:app)中添加以下代碼)
apply plugin: 'com.android.application'
//添加以下代碼,應用apt插件
apply plugin: 'com.neenbedankt.android-apt'
...
dependencies {
...
compile 'com.google.dagger:dagger:2.4'
apt 'com.google.dagger:dagger-compiler:2.4'
//java註解
compile 'org.glassfish:javax.annotation:10.0-b28'
...
}
複製代碼
下面用一個栗子來講明,如何使用Dagger2,須要說明的是,這個栗子是基於mvp模式的,因此若是還不瞭解mvp的話,能夠先去了解mvp,再繼續看下面的內容。
在mvp中,最多見的一種依賴關係,就是Activity持有presenter的引用,並在Activity中實例化這個presenter,即Activity依賴presenter,presenter又須要依賴View接口,從而更新UI,就像下面這樣:
public class MainActivity extends AppCompatActivity implements MainContract.View {
private MainPresenter mainPresenter;
...
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//實例化presenter 將view傳遞給presenter
mainPresenter = new MainPresenter(this);
//調用Presenter方法加載數據
mainPresenter.loadData();
...
}
}
public class MainPresenter {
//MainContract是個接口,View是他的內部接口,這裏看作View接口便可
private MainContract.View mView;
MainPresenter(MainContract.View view) {
mView = view;
}
public void loadData() {
//調用model層方法,加載數據
...
//回調方法成功時
mView.updateUI();
}
複製代碼
這樣Activity與presenter僅僅耦合在了一塊兒,當須要改變presenter的構造方式時,須要修改這裏的代碼。若是用依賴注入的話,是這樣的:
public class MainActivity extends AppCompatActivity implements MainContract.View {
@Inject
MainPresenter mainPresenter;
...
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerMainComponent.builder()
.mainModule(new MainModule(this))
.build()
.inject(this);
//調用Presenter方法加載數據
mainPresenter.loadData();
...
}
}
public class MainPresenter {
//MainContract是個接口,View是他的內部接口,這裏看作View接口便可
private MainContract.View mView;
@Inject
MainPresenter(MainContract.View view) {
mView = view;
}
public void loadData() {
//調用model層方法,加載數據
...
//回調方法成功時
mView.updateUI();
}
@Module
public class MainModule {
private final MainContract.View mView;
public MainModule(MainContract.View view) {
mView = view;
}
@Provides
MainView provideMainView() {
return mView;
}
}
@Component(modules = MainModule.class)
public interface MainComponent {
void inject(MainActivity activity);
}
複製代碼
額,貌似變得更復雜了,還不如不用Dagger2呢。不過仔細想一想也是能夠理解的,直接組合方式雖然簡單,可是具備耦合性,爲了解決這種耦合,可能就會多產生一些輔助類,讓這種直接的依賴關係,變爲間接,下降耦合。跟大多數設計模式同樣,爲了達到高內聚低耦合,每每會有不少接口與類,Daager2也是如此,雖然看似複雜了些,不過這在軟件工程中是值得的。下面,就來分析下上面代碼是什麼意思。
咱們先看MainActivity裏的代碼,以前是直接聲明MainPresenter,如今在聲明的基礎上加了一個註解@Inject,代表MainPresenter是須要注入到MainActivity中,即MainActivity依賴於MainPresenter,這裏要注意的是,使用@Inject時,不能用private修飾符修飾類的成員屬性。
而後咱們在MainPresenter的構造函數上一樣加了@Inject註解。這樣MainActivity裏的mainPresenter與他的構造函數創建了某種聯繫。這種聯繫咱們能夠這樣理解,當看到某個類被@Inject標記時,就會到他的構造方法中,若是這個構造方法也被@Inject標記的話,就會自動初始化這個類,從而完成依賴注入。
而後,他們之間並不會憑空創建起聯繫,所以咱們想到,確定須要一個橋樑,將他們鏈接起來,也就是下面要介紹的Component。
Component是一個接口或者抽象類,用@Component註解標註(這裏先無論括號裏的modules),咱們在這個接口中定義了一個inject()方法,參數是Mainactivity。而後rebuild一下項目,會生成一個以Dagger爲前綴的Component類,這裏是DaggerMainComponent,而後在MainActivity裏完成下面代碼.
DaggerMainComponent.builder()
.mainModule(new MainModule(this))
.build()
.inject(this);
複製代碼
此時Component就將@Inject註解的mainPresenter與其構造函數聯繫了起來。此時,看到這裏,若是是初學者的話,必定會很是迷惑,到底是怎麼創建起聯繫的,實例化過程在哪?別急,後面會講解這個過程原理的。
此時咱們已經完成了presenter的注入過程,可是咱們發現還有一個MainModule類,這個類是作什麼的?MainModlue是一個註解類,用@Module註解標註,主要用來提供依賴。等等,剛纔經過@Inject就能夠完成依賴,爲何這裏還要用到Module類來提供依賴?之因此有Module類主要是爲了提供那些沒有構造函數的類的依賴,這些類沒法用@Inject標註,好比第三方類庫,系統類,以及上面示例的View接口。
咱們在MainModule類裏聲明瞭MainContract.View成員屬性,在構造方法裏將外界傳進來的view賦值給mView,並經過一個@Provides標註的以provide開頭的方法,將這個view返回,這個以provide開頭的方法就是提供依賴的,咱們能夠建立多個方法來提供不一樣的依賴。那麼這個類到底是怎麼做用的?能夠想到上面提到的@Component註解括號裏的東西了。就是下面這個
@Component(modules = MainModule.class)
public interface MainComponent {
void inject(MainActivity activity);
}
複製代碼
因此Module要發揮做用,仍是要依靠於Component類,一個Component類能夠包含多個Module類,用來提供依賴。咱們接着看下面這段代碼:
DaggerMainComponent.builder()
.mainModule(new MainModule(this))
.build()
.inject(this);
複製代碼
這裏經過new MainModule(this)將view傳遞到MainModule裏,而後MainModule裏的provideMainView()方法返回這個View,當去實例化MainPresenter時,發現構造函數有個參數,此時會在Module裏查找提供這個依賴的方法,將該View傳遞進去,這樣就完成了presenter裏View的注入。
咱們來從新理一遍上面的注入過程,首先弄清楚如下幾個概念:
接着咱們從新回顧一下上面的注入過程:首先MainActivity須要依賴MainPresenter,所以,咱們在裏面用@Inject對MainPresenter進行標註,代表這是要注入的類。而後,咱們對MainPresenter的構造函數也添加註解@Inject,此時構造函數裏有一個參數MainContract.View,由於MainPresenter須要依賴MainContract.View,因此咱們定義了一個類,叫作MainModule,提供一個方法provideMainView,用來提供這個依賴,這個MainView是經過MainModule的構造函數注入進來的,接着咱們須要定義Component接口類,並將Module包含進來,即
@Component(modules = MainModule.class)
public interface MainComponent {
void inject(MainActivity activity);
}
複製代碼
同時裏面聲明瞭一個inject方法,方法參數爲ManActivity,用來獲取MainActivity實例,以初始化在裏面聲明的MainPresenter
DaggerMainComponent.builder()
.mainModule(new MainModule(this))
.build()
.inject(this);
複製代碼
此時,注入過程就完成了,或許到此時,仍是會有一些疑惑,由於咱們看不到實例化的過程在哪裏,爲何要這樣去寫代碼,因此下面,就基於這個實例,分析Dagger2內部究竟作了什麼。
Dagger2與其餘依賴注入框架不一樣,它是經過apt插件在編譯階段生成相應的注入代碼,下面咱們就具體看看Dagger2生成了哪些注入代碼?
咱們先看MainPresenter這個類,在這個類中咱們對構造方法用了@Inject標註,而後Rebuild Project,Dagger2會在/app/build/generated/apt/debug/目錄下生成一個對應的工廠類MainPresenter_Factory,咱們看下面具體代碼(爲了方便理解,我把MainPresenter也貼了出來)
public class MainPresenter {
MainContract.View mView;
@Inject
MainPresenter(MainContract.View view) {
mView = view;
}
}
public final class MainPresenter_Factory implements Factory<MainPresenter> {
private final Provider<MainContract.View> viewProvider;
public MainPresenter_Factory(Provider<MainContract.View> viewProvider) {
assert viewProvider != null;
this.viewProvider = viewProvider;
}
@Override
public MainPresenter get() {
return new MainPresenter(viewProvider.get());
}
public static Factory<MainPresenter> create(Provider<MainContract.View> viewProvider) {
return new MainPresenter_Factory(viewProvider);
}
}
複製代碼
對比MainPresenter,咱們發如今MainPre_Factory裏也生成了對應的代碼。首先是viewProvide,這是一個Provider類型,泛型參數就是咱們的MainContract.View,接着經過構造方法,對viewProvider進行實例化。其實這裏有個疑惑,上面的成員屬性爲何不直接是MainContract.View,而是Provider類型?看到provider咱們應該想到這個MainContract.View是一個依賴,而依賴的提供者是MainModule,所以這個viewProvider必定是由MainModul提供的。咱們接着看下面的get()方法,看到這個方法,我想咱們有點恍然大悟的感受,原來MainPresenter的實例化就在這裏,構造函數裏的參數就是咱們依賴的MainContract.View,它是由viewProvider經過get()提供。接着是一個create()方法,而且有一個參數viewProvider,用來建立這個MainPresenter_Factory類。
上面咱們得出,viewProvider是由MainModule提供的,因此咱們接着看MainModule所對應的注入類。
@Module
public class MainModule {
private final MainContract.View mView;
public MainModule(MainContract.View view) {
mView = view;
}
@Provides
MainContract.View provideMainView() {
return mView;
}
}
public final class MainModule_ProvideMainViewFactory implements Factory<MainContract.View> {
private final MainModule module;
public MainModule_ProvideMainViewFactory(MainModule module) {
assert module != null;
this.module = module;
}
@Override
public MainContract.View get() {
return Preconditions.checkNotNull(
module.provideMainView(), "Cannot return null from a non-@Nullable @Provides method");
}
public static Factory<MainContract.View> create(MainModule module) {
return new MainModule_ProvideMainViewFactory(module);
}
}
複製代碼
看到上面的類名,咱們發現了一種對應關係,在MainModule中定義的@Provides修飾的方法會對應的生成一個工廠類,這裏是MainModule_ProvideMainViewFactory。咱們看到這個類裏有一個get()方法,其中調用了MainModule裏的provideMainView()方法來返回咱們所須要的依賴MainContract.View。還記得在MainPresenter_Factory裏的get()方法中,實例化MainPresenter時候的參數viewProvider.get()嗎?到這裏咱們就明白了,原來那個viewProvider就是生成的MainModule_ProvideMainViewFactory,而後調用了其get()方法,將咱們須要的MainContract.View注入到MainPresenter裏。
看到這裏咱們應該明白了MainPresenter的實例化過程。MainPresenter會對應的有一個工廠類,在這個類的get()方法中進行MainPresenter建立,而MainPresenter所須要的View依賴,是由MainModule裏定義的以provide開頭的方法所對應的工廠類提供的。
雖然咱們明白了實例化的建立過程,可是此時仍是有點疑惑,MainPresenter_Factory的建立是由create()完成的,那麼crate是在哪調用的,此時建立的MainPresenter實例是怎麼跟@Inject註解的MainPresenter關聯起來的,我想你已經知道了答案,沒錯就是Component。前面說過Component是鏈接@Module和@Inject的橋樑,因此上面的疑惑就要到編譯後Component所對應的類中尋找答案。
@Component(modules = MainModule.class)
public interface MainComponent {
void inject(MainActivity activity);
}
public final class DaggerMainComponent implements MainComponent {
private Provider<MainContract.View> provideMainViewProvider;
private Provider<MainPresenter> mainPresenterProvider;
private MembersInjector<MainActivity> mainActivityMembersInjector;
private DaggerMainComponent(Builder builder) {
assert builder != null;
initialize(builder);
}
public static Builder builder() {
return new Builder();
}
@SuppressWarnings("unchecked")
private void initialize(final Builder builder) {
this.provideMainViewProvider = MainModule_ProvideMainViewFactory.create(builder.mainModule);
this.mainPresenterProvider = MainPresenter_Factory.create(provideMainViewProvider);
this.mainActivityMembersInjector = MainActivity_MembersInjector.create(mainPresenterProvider);
}
@Override
public void inject(MainActivity activity) {
mainActivityMembersInjector.injectMembers(activity);
}
public static final class Builder {
private MainModule mainModule;
private Builder() {}
public MainComponent build() {
if (mainModule == null) {
throw new IllegalStateException(MainModule.class.getCanonicalName() + " must be set");
}
return new DaggerMainComponent(this);
}
public Builder mainModule(MainModule mainModule) {
this.mainModule = Preconditions.checkNotNull(mainModule);
return this;
}
}
}
複製代碼
從上面代碼看到定義的MainComponent會生成一個對應的DaggerMainComponent,而且實現了MainComponent裏的方法。咱們看到代碼中又出現了Provide類型的成員屬性,前面說過這個Provide類型就是所提供的依賴,咱們在看它們是在哪實例化的。咱們看到有一個initialize()方法
@SuppressWarnings("unchecked")
private void initialize(final Builder builder) {
this.provideMainViewProvider = MainModule_ProvideMainViewFactory.create(builder.mainModule);
this.mainPresenterProvider = MainPresenter_Factory.create(provideMainViewProvider);
this.mainActivityMembersInjector = MainActivity_MembersInjector.create(mainPresenterProvider);
}
複製代碼
看到這估計就明白了剛纔的疑惑。首先建立了MainModule_ProvideMainViewFactory實例,用來提供MainContract.View依賴。這裏可能有個小疑惑,create()方法返回的是Factory類型,而provideMainViewProvider是個Provider類型,其實看源碼就明白了,Factory繼承自Provider。
public interface Factory<T> extends Provider<T> {
}
複製代碼
而後將provideMainViewProvider傳遞到MainPresenter_Factory裏,即就是前面講到的viewProvider。接着將這個mainPresenterProvider又傳遞到MainActivity_MembersInjector中進行實例化,咱們看到這個類前面是MainActivity開頭,所以能夠想到是MainActivity對應得注入類,咱們後面再分析這個類。
接着是咱們在MainComponent裏定義的Inject方法的實現,這裏調用了mainActivityMembersInjector.injectMembers(activity)方法,將咱們的MainActivity注入到該類中。
接着就是Builder內部類,用來建立咱們的module以及自身實例。因此在DaggerMainComponent裏主要用來初始化依賴,而真正的將這些依賴於Inject關聯起來的就是剛纔的MainActivity_MembersInjector類,咱們看看這個類裏作了什麼。
public final class MainActivity_MembersInjector implements MembersInjector<MainActivity> {
private final Provider<MainPresenter> mainPresenterProvider;
public MainActivity_MembersInjector(Provider<MainPresenter> mainPresenterProvider) {
assert mainPresenterProvider != null;
this.mainPresenterProvider = mainPresenterProvider;
}
public static MembersInjector<MainActivity> create(
Provider<MainPresenter> mainPresenterProvider) {
return new MainActivity_MembersInjector(mainPresenterProvider);
}
@Override
public void injectMembers(MainActivity instance) {
if (instance == null) {
throw new NullPointerException("Cannot inject members into a null reference");
}
instance.mainPresenter = mainPresenterProvider.get();
}
public static void injectMainPresenter(
MainActivity instance, Provider<MainPresenter> mainPresenterProvider) {
instance.mainPresenter = mainPresenterProvider.get();
}
}
複製代碼
這個類的關鍵就是injectMembers()方法,還記得這個方法在哪調用嗎?我想你確定記得,就在剛纔提到的DaggerMainComponent類中的inject()方法裏,因此這裏的instance實例是由DaggerMainComponent提供的,而後咱們看到了最關鍵的一句代碼
instance.mainPresenter = mainPresenterProvider.get();
複製代碼
看到這,我想應該一切都明白了,將mainPresenterProvider中建立好的MainPresenter實例賦值給instance(MainActivity)的成員mainPresenter,這樣咱們用@Inject標註的mainPresenter就獲得了實例化,接着就能夠在代碼中使用了。
到這裏,就分析完了Dagger2的注入過程,若是不去看這些生成的類,就很難理解整個過程到底是怎麼發生的,從而致使仍是不知道怎麼去使用這個依賴注入框架。因此重點去理解這個內部實現原理是很是重要的,剛開始學的時候也是一臉懵逼,總搞不太清之間的關係,不知道究竟怎麼寫,弄懂了整個前因後果後,發現就知道怎麼去運用了。
關於Dagger2的其餘使用就很少講了,能夠看其餘的優秀博客,我會再後面附上連接,方便學習。Dagger2就是一個依賴注入工具,至於怎麼使用徹底在我的,因此沒必要糾結到底怎麼去寫纔是正確的,只要弄懂原理,靈活運用,可以作到儘量解耦,適合本身的業務就是最好的寫法。
感謝: