Dagger2入門教程

1、Android依賴注入簡介

1.1 依賴注入(IOC:Inversion of Control)

(1)依賴注入概念
依賴注入將來單說就是非自己主動初始化依賴,而通過外部來傳入依賴的方式,我們就稱爲依賴注。舉例來說:如下面的代碼所示,A是依賴注入的例子,B是非依賴注入的例子。汽車(Car)依賴輪胎(Tyre)的資源。

  • 如果在Car內部自己去new一個Tyre的資源(如B所示),那麼Car就和Tyre產生了前的依賴,既Car->Tyre。如果Tyre的型號發生了改變,那麼Car的代碼就需要跟隨發生改變。
  • 但是如果Car不直接new一個Tyre,而是依賴於外部傳入的Tyre,那麼Car就解除了Tyre的依賴,不管外部的Tyre怎麼改變,Car的代碼永遠不會發生改變。

【A:依賴注入的例子】

public class Car {
    ...
    // 輪胎
    Tyre tyre;
    ...
    public Car(Tyre tyre) {
        this.tyre = tyre;
    }
}

【B:非依賴注入的例子】

public class Car{
    ...
    // 輪胎
    Tyre tyre;
    ...
    public Car(Tyre tyre) {
        this.tyre = new Tyre();
    }
}

(2)Ioc容器:負責依賴注入控制器
如A所示,上文已經說了Car的實現依賴於外界傳入的Tyre。那麼負責傳入這個資源的角色就是Ioc容器。Ioc容器本身也不實際產生資源,它會請求其他的對象來生成資源。由此來看,Car解除了和Tyre的依賴,而和Ioc產生了依賴:Car->Ioc容器。這樣的好處是,如果Car依賴於多個對象(方向盤,輪胎,地盤等等),那麼Car不必要和那麼多對象產生直接的依賴,而只需要依賴ioc容器即可。不管Car需要什麼資源,它只需要找ioc容器要即可。由此可以看出,依賴注入就是要解決對象之間的依賴關係,即由對象->對象的直接依賴,轉化到對象->ioc的依賴。

(3)依賴注入的好處

  • 解耦,解除對象之間的依賴關係。
  • 因爲已經解耦,所以方便做單元測試,尤其是 Mock 測試
  • 其他

1.2 Android依賴注入

在Java裏面,存在大量的依賴注入的框架,如Spring等等。在Dagger2出來之前,Android也存在一些依賴注入框架如Dagger1。其均是依靠反射來實現依賴注入,因此會帶來一定的性能問題,因此這些框架並沒有在Android中流行起來。

Dagger2通過APT技術,通過在編譯的時候生成注入的工廠類代碼,解決了性能問題,因此Dagger2開始在Android上發揚光大。

2、dagger2原理

2.1 dagger2原理簡析

Dagger2實現原理如C圖所示,其主要由三部分組成

  • 被注入對象:有屬性需要被設置
  • module:對象的生成器
  • component:對象屬性的設置器(對應上面的Ioc控制器)

【C:dagger2實現原理】

總結起來就是,當被注入的對象需要Component去給他設置屬性的時候,Component就會去找它的Module去生成該對象。如果Module完成不了這件事情,Component就會去找它依賴的Component(Dependency Component)去給他生成該對象。Dependency Component本身也沒有生成對象能力,其就依賴它的Module去生成該對象

2.2 dagger2常用註解

在瞭解Dagger2註解之前我們首先記住2個東西:

  1. Dagger2所有對象匹配都是按照返回類型來匹配,與函數命名無關
  2. Dagger2的Scope的大小都是我們人爲賦予的,Scope的大小不是名字決定的,而是Component之間的依賴關係決定的。

在2.1中所提的三種角色都是通過註解來完成的,dagger2常用註解如下:

(1)Component使用的註解

  • @Component
    • 作用對象:Class
    • 表明該class是個Component
  • @Scope
    • 作用對象:Class
    • 指明Component 範圍,其依賴的Model如果用@Scope修飾,必須與Component 的Scope相同

Component示例如下所示:

  • 因爲ActivityComponent用@Component修飾了,因此示一個Component,其可用來注入一個對象(給對象屬性賦值)
  • 其依包含的Module是ActivityModule.class,並且可以包含Module
  • 其依賴於AppComponent.class,也可以依賴多個Component
@BigScoped
@Component(modules = {ActivityModule.class},dependencies = {AppComponent.class})
public interface ActivityComponent {
    void inject(Dagger2Activity dagger2Activity);
    ActivityScopeObj activityScopeObj();
    ActivityObj activityObj();
}

(2)Module使用的註解

  • @Module
    • 作用對象:Class
    • 表明該Class是個對象的生成器
  • @Provider
    • 作用對象:Method
    • 表明該方法能對外提供對象:按照返回的類型匹配
  • @Scope
    • 作用對象:Method
    • 表明該方法在Scope範圍內是個單例

Module示例:

  • 其用@Module修飾,表明其示一個Module,屬於某一個Component
  • 只有用@Provides修飾的函數纔是Module能提供的對象,其按照返回的類型來進行匹配,與函數命名無關
  • @BigScoped:表示一個ActivityComponent的實例只會返回一個ActivityScopeObj的對象。原理可參見:
@Module
public class ActivityModule {

    @Provides
    public ActivityObj provideActivityObj() {
        return new ActivityObj();
    }

    @Provides
    public FragmentNotVisibleObj provideActivityNotVisibleObj() {
        return new FragmentNotVisibleObj();
    }

    @BigScoped
    @Provides
    public ActivityScopeObj provideActivityScopeObj() {
        return new ActivityScopeObj();
    }

}

(3)被注入對象使用的註解

  • @Inject
    • 作用對象:Field
    • 表明該屬性依賴Component進行注入

代碼示例:

public class Dagger2Activity extends AppCompatActivity implements ActivityDataProvider {

    @Inject
    ActivityObj mActivityObj;
    @Inject
    FragmentNotVisibleObj mActivityNotVisibleObj;
    @Inject
    ActivityScopeObj mActivityScopeObj;

    ActivityComponent component = DaggerActivityComponent.builder()
        .activityModule(new ActivityModule())
        .build();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_dagger2_test);
        // 使用component來給Dagger2Activity的屬性賦值
        component.inject(this);

再看看ActivityComponent生命的inject方法

  • inject方法示Component留給被注入對象將自己傳進來的入口,這個名字可隨便命名
  • 其參數必須對應一個具體的對象,不能是基類,也不能是父類
public interface ActivityComponent {
    void inject(Dagger2Activity dagger2Activity);

3、dagger2案例

案例地址:dagger案例源碼

3.1 申明Scope

// BigScope
@Documented
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface BigScoped {

}
// SmallScope
@Documented
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface SmallScoped {
}

3.2 Dagger2Activity的注入

(1)Dagger2Activity

  • 其依賴於ActivityComponent來給他注入三個屬性mActivityObj,mActivityNotVisibleObj和mActivityScopeObj
  • 其中mActivityScopeObj在Module裏面用Scope修飾了,因此一個ActivityComponent的實例只會返回一個mActivityScopeObj對象
public class Dagger2Activity extends AppCompatActivity implements ActivityDataProvider {

    @Inject
    ActivityObj mActivityObj;
    @Inject
    FragmentNotVisibleObj mActivityNotVisibleObj;
    @Inject
    ActivityScopeObj mActivityScopeObj;

    ActivityComponent component = DaggerActivityComponent.builder()
        .activityModule(new ActivityModule())
        .build();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_dagger2_test);
        // 使用component來給Dagger2Activity的屬性賦值
        component.inject(this);
        Dagger2Fragment dagger2Fragment = new Dagger2Fragment();
        dagger2Fragment.setDataProvider(this);
        getFragmentManager().beginTransaction()
            .addToBackStack(null)
            .replace(R.id.container, dagger2Fragment)
            .commit();
    }

    @Override
    public ActivityComponent getComponent() {
        return component;
    }

    @Override
    public ActivityScopeObj getActivityScopeObj() {
        return mActivityScopeObj;
    }

}

(2)ActivityComponent

  • 使用ActivityModule來生成對象
  • 同時依賴AppComponent來生成對象
  • 其他參見注釋:註釋很重要
@BigScoped
@Component(modules = {ActivityModule.class},dependencies = {AppComponent.class})
public interface ActivityComponent {
    // 暴露接口給Dagger2Activity,讓其傳入自己,好給Dagger2Activity屬性賦值
    void inject(Dagger2Activity dagger2Activity);
    // 暴露給其他Component的接口,只有暴露的接口,其他的Component纔可以依賴於它創建對象,按照返回類型匹配,與函數名無關
    ActivityScopeObj activityScopeObj();
    ActivityObj activityObj();
}

(3)ActivityModule
- ActivityModule可以提供三種對象ActivityObj,FragmentNotVisibleObj,ActivityScopeObj
- ActivityScopeObj上文已經解釋了
- 由於FragmentNotVisibleObj沒有在ActivityComponent中暴露,因此FragmentComponent(參見3.2-2和3.3)不能依賴其提供FragmentNotVisibleObj類型的對象

@Module
public class ActivityModule {

    @Provides
    public ActivityObj provideActivityObj() {
        return new ActivityObj();
    }

    @Provides
    public FragmentNotVisibleObj provideActivityNotVisibleObj() {
        return new FragmentNotVisibleObj();
    }

    @BigScoped
    @Provides
    public ActivityScopeObj provideActivityScopeObj() {
        return new ActivityScopeObj();
    }
}

【Dagger2Activity的整體注入過程如下圖】
從圖中可以看出dagger2的實現模型和上面介紹的Ioc控制反轉模型表現一致
這裏寫圖片描述

3.2 Dagger2Fragment的注入

(1)Dagger2Fragment

  • 需要注入三個對象
    • mActivityObj由ActivityComponent生成
    • mActivityScopeObj由ActivityComponent生成
    • mFragmentObj有FragmentComponent生成
    • 其他參見注釋
public class Dagger2Fragment extends android.app.Fragment {


    ActivityDataProvider mProvider;

    @Inject
    ActivityObj mActivityObj;
    @Inject
    ActivityScopeObj mActivityScopeObj;
    @Inject
    FragmentObj mFragmentObj;

    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable final ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.dagger2_fragment, container, false);
        FragmentComponent component = DaggerFragmentComponent.builder()
            .fragmentModule(new FragmentModule())
            // 依賴於ActivityComponent,是由Activity傳進來的,因此次數使用的activityComponent和Dagger2Activity使用的Component是同一個對象
            // 因此 mActivityScopeObj和mProvider.getActivityScopeObj()得到的是同一個對象
            .activityComponent(mProvider.getComponent())
            .build();
        component.inject(this);
        // true
        Logger.d("ActivityScope single instance:" + (mActivityScopeObj == mProvider.getActivityScopeObj()));
        // false
        Logger.d("not Scope Object:" + (mActivityObj == mFragmentObj.mActivityObj));
        return view;
    }

    public void setDataProvider(ActivityDataProvider provider) {
        mProvider = provider;
    }
}

(2)FragmentComponent

@Component(modules = {FragmentModule.class},dependencies = {ActivityComponent.class})
@SmallScoped
public interface FragmentComponent {
    void inject(Dagger2Fragment dagger2Fragment);
}

(3)FragmentObj

public class FragmentObj {

    @Inject
    ActivityObj mActivityObj;

    // 用@Inject修飾構造和在Module用@Provide修飾等價
    @Inject
    public FragmentObj() {

    }
}

4、dagger2入門參見疑惑問題

(1)查找對象按照什麼匹配?

按照類型匹配

  • 兩個函數是完全等價的
@Module
public class ActivityModule {

    @Provides
    public ActivityObj provideActivityObj() {
        return new ActivityObj();
    }
    @Provides
    public ActivityObj ActivityObj() {
        return new ActivityObj();
    }

(2)Scope之間的依賴關係怎麼確定?

按照其修飾的Component間的依賴關係決定,還是上面的例子:

@Component(modules = {FragmentModule.class},dependencies = {ActivityComponent.class})
@SmallScoped
public interface FragmentComponent {
    void inject(Dagger2Fragment dagger2Fragment);
}

@BigScoped
@Component(modules = {ActivityModule.class},dependencies = {AppComponent.class})
public interface ActivityComponent {
    void inject(Dagger2Activity dagger2Activity);
    ActivityScopeObj activityScopeObj();
    ActivityObj activityObj();
}

因爲FragmentComponent–>ActivityComponent。因此,可以得出ActivityComponent實例生命週期長於FragmentComponent的實例(不然,FragmentComponent要傳入AppComponent依賴的時候,沒有可以賦的值),因此可以得出SmallScope和BiggerScope之間存在如下關係。

Scope實現原理可以參見:dagger2源碼分析

(3)其他更多工程上使用的問題可以參見ppt:dagger2源碼分析

參考文獻

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