Dagger2入門教程

一、Android依賴注入簡介

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

(1)依賴注入概念
依賴注入未來單說就是非本身主動初始化依賴,而經過外部來傳入依賴的方式,咱們就稱爲依賴注。舉例來講:以下面的代碼所示,A是依賴注入的例子,B是非依賴注入的例子。汽車(Car)依賴輪胎(Tyre)的資源。android

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

【A:依賴注入的例子】git

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

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

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沒必要要和那麼多對象產生直接的依賴,而只須要依賴Ico容器便可。無論Car須要什麼資源,它只須要找Ico容器要便可。由此能夠看出,依賴注入就是要解決對象之間的依賴關係,即由對象->對象的直接依賴,轉化到對象->Ico的依賴。app

<img src="https://img-blog.csdn.net/201...; width="60%">框架

(3)依賴注入的好處ide

  • 解耦,解除對象之間的依賴關係。
  • 由於已經解耦,因此方便作單元測試,尤爲是 Mock 測試
  • 其餘

1.2 Android依賴注入

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

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

二、dagger2原理

2.1 dagger2原理簡析

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

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

【C:dagger2實現原理】
<img src="https://img-blog.csdn.net/201...; width="60%">單元測試

總結起來就是,當被注入的對象須要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);

三、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() {

    }
}

四、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之間存在以下關係。
<img src="https://img-blog.csdn.net/201...; width="70%">

(3)其餘更多工程上使用的問題能夠參見ppt:dagger2源碼分析

參考文獻

相關文章
相關標籤/搜索