dagger2框架解析

Dagger2定義

Dagger2 是一個Android依賴注入框架,由谷歌開發,最早的版本Dagger1 由Square公司開發。依賴注入框架主要用於模塊間解耦,提高代碼的健壯性和可維護性。Dagger 這個庫的取名不僅僅來自它的本意“匕首”,同時也暗示了它的原理。Jake Wharton 在對 Dagger 的介紹中指出,Dagger 即 DAG-er,這裏的 DAG 即數據結構中的 DAG——有向無環圖(Directed Acyclic Graph)。也就是說,Dagger 是一個基於有向無環圖結構的依賴注入庫,因此Dagger的使用過程中不能出現循環依賴。


那什麼是依賴呢?如果在 Class A 中,有 Class B 的實例,則稱 Class A 對 Class B 有一個依賴。例如下面類 Human 中用到一個 Father 對象,我們就說類 Human 對類 Father 有一個依賴。


那什麼又是依賴注入呢,依賴注入就是非自己主動初始化依賴,而通過外部來傳入依賴的方式,簡單來說就是不使用 new 來創建依賴對象。使用 Dagger2 創建依賴對象,我們就不用手動初始化了。個人認爲 Dagger2 和 MVP 架構是比較不錯的搭配,Activity 依賴的 Presenter 可以使用該DI框架直接生成,實現解耦,簡單的使用方式如下:


dagger2到底有哪些好處?
1.增加開發效率、省去重複的簡單體力勞動

首先new一個實例的過程是一個重複的簡單體力勞動,dagger2完全可以把new一個實例的工作做了,因此我們把主要精力集中在關鍵業務上、同時也能增加開發效率上。

省去寫單例的方法,並且也不需要擔心自己寫的單例方法是否線程安全,自己寫的單例是懶漢模式還是餓漢模式。因爲dagger2都可以把這些工作做了。

2.更好的管理類實例

每個app中的ApplicationComponent管理整個app的全局類實例,所有的全局類實例都統一交給ApplicationComponent管理,並且它們的生命週期與app的生命週期一樣。

每個頁面對應自己的Component,頁面Component管理着自己頁面所依賴的所有類實例。

因爲Component,Module,整個app的類實例結構變的很清晰。

3.解耦

假如不用dagger2的話,一個類的new代碼是非常可能充斥在app的多個類中的,假如該類的構造函數發生變化,那這些涉及到的類都得進行修改。設計模式中提倡把容易變化的部分封裝起來。
假如是通過用Inject註解標註的構造函數創建類實例,則即使構造函數變的天花亂墜,我們基本上都不需要修改任何代碼。

假如是通過工廠模式Module創建類實例,Module其實就是把new類實例的代碼封裝起來,這樣即使類的構造函數發生變化,只需要修改Module即可。

4.利於分工和維護拓展,只需提供接口文檔,調用者無需關心構造函數參數


配置
在 APP gradle 配置文件中


由於 Dagger 使用 apt 生成代碼,在Project gradle中還需要加入:




結構



Dagger2主要分爲4部分:
對象,被調用者, 被依賴注入的類,一般爲mvp模式中的p,即presenter
對象的實例化,叫做容器Module
溝通橋樑:將容器和調用者連接起來Component
調用者:需要實例化對象的類,即activity、fragment

對象
public class Person {
   @Inject
    public Person(){
        Log.i("dagger","person create!!!");
    }
}

容器
@Module   //提供依賴對象的實例
public class MainModule {

    @Provides // 關鍵字,標明該方法提供依賴對象
    Person providerPerson(){
        //提供Person對象
        return new Person();
    }
}

橋樑
@Component(modules = MainModule.class)  // 作爲橋樑,溝通調用者和依賴對象庫
public interface MainComponent {

    //定義注入的方法
    void inject(MainActivity activity);

}

調用者

public class MainActivity extends AppCompatActivity{

    @Inject   //標明需要注入的對象
    Person person;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // 構造橋樑對象
        MainComponent component = DaggerMainComponent.builder().mainModule(new MainModule()).build();

        //注入
        component.inject(this);

    }
}

解釋下連接橋樑初始化



MainComponent是定義的橋樑,build後會build目錄自動生成DaggerMainComponent類,類名由Dagger和MainComponent組成,接下來的.mainModule 方法名也不是固定的,
是由MainComponent中modules = MainModule.class決定的。總之橋樑的實例化是會根據橋樑的類名和類裏面依賴的父類、使用的容器多個因素決定的。


DaggerMainComponent類




MainComponent




邏輯描述

1.    在Application構建全局component,app初始化即執行
注意要在AnroidMainfest.xml中給application標明執行的類名,即 android:name=".app.RXRetrofitApplication"



全局AppComonent裏面註明連接的容器以及向子容器傳遞的對象,inject表示調用者

如上所示,向子橋樑傳遞了MainViewInteraction對象,這個對象見InteractorModule:




2.    子橋樑 繼承主橋樑(AppComonent),在子橋樑裏面的容器MainActivityModule可以使用主橋樑傳遞過來的對象




依賴注入有個特性,被依賴注入的對象若有參數傳遞,參數也必須依賴注入,MainViewInteraction有參數WeatherApiService, 依賴注入WeatherApiService, 然後再依賴注入Retrofit,Retrofit裏return RestApiAdapter.getInstance(), RestApiAdapter.getInstance()即爲Retrofit網絡訪問相關邏輯。



3.    Activity依賴注入Presenter對象,構建本activity的橋樑


MainActivityComponent繼承appComponent,連接mainActivityModule
,並且將本activity作爲參數傳進去。在mainActivityModule裏標註了MainViewPresenter,執行new MainViewPresenterImpl(mainView, mainViewInteraction),本activity 賦值給了mainView,回調mainView接口中的showWeatherInfor,mainViewInteraction參數封裝了Retrofit
當點擊text,則把參數傳給mainViewPresenter


由於load方法加了監聽,會回調onFinished
方法,執行showWeatherInfor方法,就把指定地點的天氣信息顯示出來




註解解釋

@Module:作爲實例對象的容器。
@Provides:標註能夠提供實例化對象的方法。
@Component:作爲橋樑,注入對象的通道。
@Inject:需要注入的方法

調用者使用@Inject標識被調用者, 並構建橋樑。
橋樑使用@Component, 並標識依賴的父橋樑、連接的容器、子橋樑、將連接的容器中指定的方法傳給子橋樑中的容器幾個因子。
容器使用@Module,提供依賴對象@Provides
被調用者使用@Inject,表明被調用的方法

其中容器和被調用者設計的@Provides和@Inject,是先搜索容器中@Provides標示的對象,如果沒有,再去搜索@Inject,個人覺得都寫上更好點

@Singleton當前提供的對象將是單例模式 ,一般配合@Provides一起出現,當module出現@Singleton,其對應的Component也必須標註Singleton。

 @Provides // 關鍵字,標明該方法提供依賴對象
    @Singleton
    Person providerPerson(){
        return new Person();
    }




@Named  對象有多個構造函數,通過@named區分使用哪個。




@Scope作用域註解,通過自定義註解限定註解作用域。
比如@Singleton就是Dagger預先定義的作用域註解。


當有Component依賴於別的Component的情況時,兩者的作用域不能相同,必須定義一個新的作用域,在同一Component裏面標記的多個activity 依賴注入一個類使用的是同一個對象,如果兩個activity分別使用各自的Component對同一個類標記不同的作用域,則引用的是不同的對象。


@Subcomponent 繼承,Subcomponent其功能效果優點類似component的dependencies。但是使用@Subcomponent不需要在父component中顯式添加子component需要用到的對象,只需要添加返回子Component的方法即可,子Component能自動在父Component中查找缺失的依賴。
//父Component:
@PerApp
@Component(modules=××××)
public AppComponent{
    SubComponent subcomponent();  //1.只需要在父Component添加返回子Component的方法即可
}

//子Component:
@PerAcitivity   //2.注意子Component的Scope範圍小於父Component
@Subcomponent(modules=××××)   //3.使用@Subcomponent
public SubComponent{
    void inject(SomeActivity activity);
}

//使用
public class SomeActivity extends Activity{
    public void onCreate(Bundle savedInstanceState){
        ...
        App.getComponent().subCpmponent().inject(this);//4.調用subComponent方法創建出子Component
    }   
}


個人感受
好處:
1.解耦,將依賴的對象實例集中在容器中,供activity或者fragment調用,一般情況會有多個地方訪問同一對象,對象構造函數發生變化,只需在容器中修改即可。
2.調用者不需要關心依賴注入對象的初始化,只需關心該對象提供什麼功能,使用了相應的什麼函數。
3.提供單例模式,使用@singleton就搞定了。
4.結合mvp使用,進一步進行了解耦,利於測試和維護。

缺點:
1.工作量變大,相當於把一個簡單的new過程 分解了橋樑、容器、調用與被調用者,還有多個註解,目前沒發現這種方式提高了性能的說法。
2.主要是對P層依賴注入,也就是說如果不是mvp架構,dagger就沒多大意義了。

相關鏈接
Demo下載
https://github.com/tuozhaobing/RXRetrofitDaggerMvpDemo.git
https://github.com/niuxiaowei/Dagger2Sample.git

資料學習
http://www.cnblogs.com/zhuyp1015/p/5119727.html
http://blog.csdn.net/u014315849/article/details/51566388
http://www.jianshu.com/p/a23c50cb4094

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章