1. 程式人生 > >Android Dagger2依賴注入庫使用詳解

Android Dagger2依賴注入庫使用詳解

導語

    依賴注入是一種面向物件的程式設計模式,它的出現是為了降低耦合性。可能有的人覺得之前並沒有使用過依賴注入,其實當我們在類的建構函式中通過引數引入物件或通過set方法設定類的物件時,就是依賴注入。而通過註解的方式完成的依賴注入就是本篇要講的Dagger庫的使用。

新增依賴build.gradle

  compile 'com.google.dagger:dagger:2.7'
  annotationProcessor 'com.google.dagger:dagger-compiler:2.7'

*如果,build專案丟擲“ Execution failed for task ':app:javaPreCompileDebug” 就在gradle中填寫下面程式碼。

defaultConfig {
    javaCompileOptions { annotationProcessorOptions { includeCompileClasspath = true } }
}

如何使用

1、 建立Student類:

public class Student {

    @Inject
    public Student() {
    }
}

Student類中有一個空的構造方法,並且在構造方法上面添加了一個@Inject註解。

然後,我們使用ctrl+F9(mac使用Cmd+F9)進行一次編譯,檢視專案路徑:

app\build\generated\source\apt\debug\com\mei_husky\sample_dagger2\model\Student_Factory.java

會發現編譯器自動生成了一個Student_Factory類:

@Generated(
  value = "dagger.internal.codegen.ComponentProcessor",
  comments = "https://google.github.io/dagger"
)
public enum Student_Factory implements Factory<Student> {
  INSTANCE;

  @Override
  public Student get() {
    return new Student();
  }

  public static Factory<Student> create() {
    return INSTANCE;
  }
}

一個工廠類,在通過create()建立後,每次呼叫get()方法都能獲得一個Student物件。

小結:通過@Inject註解了一個類的構造方法後,可以讓編譯器幫助我們產生一個對應的Factory類,通過這個工廠類我們可以通過簡單的get()方法獲取到Student物件。

2、呼叫Student類:

public class A01SimpleActivity extends AppCompatActivity {

    @BindView(R.id.btn_01)
    Button btn01;

    @Inject
    Student student;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_a01_simple);
        ButterKnife.bind(this);
    }

    @OnClick(R.id.btn_01)
    public void onViewClicked(View view) {
        switch (view.getId()){
            case R.id.btn_01:             
                Toast.makeText(this,student.toString(),Toast.LENGTH_SHORT).show();
            break;
        }
    }
}

Activity類中建立一個成員變數Student,按照Dagger2給我們的指示,當我們需要一個Student,我們只需要在這個成員變數上方加一個@Inject註解,編譯器會自動幫我們產生對應的程式碼,我們就可以直接使用這個Student物件了!執行程式碼,並點選Button直接報空指標異常:

這裡寫圖片描述

小結:@Inject並沒有幫助我們初始化對應的Student物件,或者說,我們的Activity並沒有使用剛才我們看到的Student_Factory類。這是因為缺少建立Activity和Student_Factory類之間的關係。

3、Module和Component使用

接下來建立Module類、Component介面:

@Module
public class A01SimpleModule {

    private A01SimpleActivity activity;

    public A01SimpleModule(A01SimpleActivity activity) {
        this.activity = activity;
    }
}
@Component(modules = A01SimpleModule.class)
public interface A01SimpleComponent {

    void inject(A01SimpleActivity activity);

}
  • Module類上方的@Module註解意味著這是一個提供資料的"模組";
  • @Component(modules = A01SimpleModule.class)註解意味著這是一個"元件" 或稱作:注射器。

繼續在之前Activty中新增下面一段程式碼,執行後呼叫成功。

public class A01SimpleActivity extends AppCompatActivity {
    @BindView(R.id.btn_01)
    Button btn01;

    @Inject
    Student student;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_a01_simple);
        ButterKnife.bind(this);
        //新添程式碼
        DaggerA01SimpleComponent.builder()
                .a01SimpleModule(new A01SimpleModule(this))
                .build()
                .inject(this);
    }

    @OnClick(R.id.btn_01)
    public void onViewClicked(View view) {
        switch (view.getId()){
            case R.id.btn_01:
                Toast.makeText(this,student.toString(),Toast.LENGTH_SHORT).show();
                break;
        }
    }
}

小結:添加了Module和Component類,然後在Activity中新增一段程式碼,被@Inject的Student類被成功依賴注入到了Activity中,並且其他地方也可以使用上面一段程式碼呼叫Student類,這樣就降低了Student的耦合度。

為什麼要使用依賴注入?

    這時候不可避免的,有些同學會有些疑問,我們需要一個Student物件,完全可以直接通過new的方式建立一個嘛?這是因為通常簡單的程式碼具有耦合性,而要想降低這樣的耦合就需要其他的輔助程式碼,其實程式碼量和耦合度這是兩碼事。試想,我們如果通過這樣的方式,在其他的檔案中建立1000個檔案使用Student物件,那麼至少要new 1000個新的Student類物件。這時,新的需求到了,Student類需要改動內部屬性和欄位, 可能需要分別對這1000個檔案中逐個修改呼叫的程式碼。如果使用Dagger2,我們只需要在Student類中做出簡單的修改即可輕鬆完成對Student的構造修改問題,達到低耦合的效果即可。

Component、Module如何理解?

假設,案例中的Activity——代表學校,Student——代表某學生。現在學校(Activity)要開學了,需要學生(Student)去報名。OK,接下來,學生肯定要在家整理行李(家代表著自動生成的Student_Factory)。雖然這位學生(Student)已經從家(Student_Factory)準備出發了,但是距離學校(Activity)還是有一段距離的,肯定需要一種交通方式(Component)。

   交通方式(Component)就是一種注入器,將學生(Student)送到學校(Activity)。即如下程式碼:

  DaggerA01SimpleComponent.builder()
          .a01SimpleModule(new A01SimpleModule(this))
          .build()
          .inject(this);

這時可能會問,Module到底幹什麼的呢?

首先,我們可以註釋調Module的程式碼,結果,發現我們依然可以正常使用Student物件。

        DaggerA01SimpleComponent.builder()
              //.a01SimpleModule(new A01SimpleModule(this))//註釋掉這行程式碼
                .build()
                .inject(this);

我們可以暫時可以這樣理解Module,它的作用就是交通方式的一種叫“公交車”。學生乘坐公交車就可以去學校,比較好理解。

然後,重寫Student類,取消了@Inject註解。

public class Student {
    //取消註解
    public Student() {
    }
}
Module類新增程式碼:
@Module
public class A01SimpleModule {

    private A01SimpleActivity activity;

    public A01SimpleModule(A01SimpleActivity activity) {
        this.activity = activity;
    }
    //下面為新增程式碼:
    @Provides
    Student provideStudent(){
        return new Student();
    }
}

最後,Component類程式碼不變,在Activity中呼叫Student物件,結果依然正常。

 DaggerA01SimpleComponent.builder()
                .a01SimpleModule(new A01SimpleModule(this))
                .build()
                .inject(this);

小結:原因是,雖然@Inject註解取消了,但是,已經在公交車(Module)上強行提供 @Providers 一個學生進行乘坐,然後學生被這種交通方式(Component)送到學校(Activity)。

總結

經過簡單的使用Dagger2,我們已經可以基本有了以下了解:

  • @Inject : 注入,被註解的構造方法會自動編譯生成一個Factory工廠類提供該類物件。
  • @Component: 注入器,類似快遞員,作用是將產生的物件注入到需要物件的容器中,供容器使用。
  • @Module: 模組,類似快遞箱子,在Component介面中通過@Component(modules = xxxx.class),將容器需要的商品封裝起來,統一交給快遞員(Component),讓快遞員統一送到目標容器中。

————————————————————————————

原始碼請看:Android 神兵利器Dagger2使用詳解(二)Module&Component原始碼分析