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原始碼分析