Dagger2 是一個依賴注入的框架,經過編譯時生成 Java 代碼,來進行依賴注入實現。java
使用前先須要瞭解依賴倒置原則及依賴注入這兩個概念。依賴倒置原則是設計模式六大原則之一,程序要依賴於抽象接口,高層模塊不該該依賴低層模塊,就好比汽車依賴於引擎,而引擎不該該依賴於汽車。依賴注入實際上是一個很簡單且經常使用的東西,就是將一個對象放進另外一個對象裏,咱們日常用 setter 或構造方法傳參將一個對象注入到這個對象,這就是依賴注入。具體看如下文章便可。android
依賴倒置原則:blog.csdn.net/zhengzhb/ar… 依賴注入:droidyue.com/blog/2015/0…設計模式
若要查看 Dagger2 相關的文檔、API,直接訪問官網便可:dagger.dev/app
@Inject:做爲依賴提供方使用:在構造函數上,經過標記構造函數讓 Dagger2 使用(Dagger2 經過 @Inject 能夠在須要這個類實例的時候來找到這個構造函數並把相關實例 new 出來)從而提供依賴;做爲依賴需求方:標記在須要依賴的變量 Dagger2 會幫助初始化,被咱們使用。框架
@Moudle:依賴提供方,負責提供依賴中所須要的對象ide
@Component:依賴注入組件,負責將依賴注入到依賴需求方。函數
@Provides:會根據返回值類型在有此註解的方法中尋找應調用的方法單元測試
未使用 Dagger2 ,通常是經過如下方式注入依賴:學習
public class Car {
public Car() {
//TODO
}
@Override
public String toString() {
return "汽車";
}
}
複製代碼
public class MainActivity extends AppCompatActivity {
private Car mCar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mCar = new Car();
Log.i(MainActivity.class.getName(), mCar.toString());
}
}
複製代碼
使用 Dagger2:測試
在 Car 類中用 @Inject 修飾須要被調用的構造函數:
public class Car {
@Inject
public Car() {
//
}
@Override
public String toString() {
return "汽車";
}
}
複製代碼
添加一個 Module 類,用於提供依賴中所須要的對象,須要用到 @Module 和 @Provider 註解:
@Module
public class CarModule {
@Provides public Car provideCar() {
return new Car();
}
}
複製代碼
添加一個 Component,表示給哪些類注入哪些對象,好比 MainActivity 類須要用到 Car 對象,那麼就能夠新建一個接口,在接口上添加註解 @Component,把 CarModule 賦上去,在接口下新增一個注入方法,把須要使用該對象的類做爲參數傳入進來,固然還能夠將其餘對象提供出去:
@Component(modules = CarModule.class)
public interface MainComponent {
void inject(MainActivity mainActivity);
}
複製代碼
而後修改 MainActivity,將直接 new 出來的 Car 對象去掉,採用 Dagger2 的方式進行依賴注入。首先在 Car 對象的聲明上加上 @inject 註解,注意此處不能聲明爲 private,由於後面生成代碼時要用到:
@Inject
Car mCar;
複製代碼
而後進行 build 生成輔助代碼,此時 apt 會生成一個 DaggerMainComponent 類,此時在 MainActivity 經過 Dagger2 將相關的依賴注入:
public class MainActivity extends AppCompatActivity {
@Inject
Car mCar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MainComponent component = DaggerMainComponent.create();
component.inject(this);
Log.i(MainActivity.class.getName(), mCar.toString());
}
}
複製代碼
還有另外一種 Component 建立模式,經過 Builder 來建立,在大型項目中這種形式可能會用得多一些,由於這樣會更靈活:
MainComponent component = DaggerMainComponent.builder().carModule(new CarModule()).build();
複製代碼
其中,Builder 裏的方法來自於 @Component 裏的 Module 值,用來指定 Module。
到此,一個簡單的使用 Demo 就完成了。其原理就是在 build 的時候生成一系列代碼,對依賴進行注入,生成的代碼用到了許多設計模式,後續能夠深刻學習。
@Named 用於區分獲取依賴的方法。從上文可知 Dagger2 尋找依賴是根據返回值進行查找的,當遇到同返回值的不一樣依賴便可經過 @Named 註解進行獲取:
@Module
public class CarModule {
@Named("normal")
@Provides
public Car provideCar() {
return new Car();
}
@Named("blue")
@Provides
public Car provideBlueCar() {
Car car = new Car();
car.setColor(Color.BLUE);
return car;
}
}
複製代碼
在 MainActivity 中在 @Inject 的地方用 @Named 加以區分:
@Inject
@Named("blue")
Car mCar;
複製代碼
即:使用 @Named 標記 Module 中生成類實例的方法,使用 @Named 標記目標類中相應類實例
@Qualifier 能夠實現與 @Named 同樣的做用,其實 @Named 就是 @Qualifier 使用的一個例子。使用 @Qualifier 須要本身定義註解,用 @Qualifier 註解定義一個 @UserThirdQualifier 註解:
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface UserThirdQualifier {
String value() default "";
}
複製代碼
@Module
public class CarModule {
@UserThirdQualifier("normal")
@Provides
public Car provideCar() {
return new Car();
}
@UserThirdQualifier("blue")
@Provides
public Car provideBlueCar() {
Car car = new Car();
car.setColor(Color.BLUE);
return car;
}
}
複製代碼
在 MainActivity 中用 @UserThirdQualifier 註解區分便可,與 @Named 如出一轍:
@Inject
@UserThirdQualifier("blue")
Car mCar;
複製代碼
在 Dagger2 中,用 @Scope 註解的類(也能夠註解方法,對應點類)能夠在當前 Component 內產生的實例是單例的。並非只有 @Singleton 註解的類纔是單例,而是全部 @Scope 註解的類,看 @Singleton 的代碼便可知:
@Scope
@Documented
@Retention(RUNTIME)
public @interface Singleton {}
複製代碼
**注意,此處的單例是指這個 Component 的生命週期下。**咱們能夠自定義做用域的粒度(好比 @PerFragment 指定在 Fragment 的生命週期內等等)。
Component 能夠被依賴(在 @Component 註解上面傳 dependencies 參數便可),@Subcomponent 也是用於管理 Component 間的依賴的。若是須要父組件的所有 Provider,這時咱們能夠用 @Subcomponent 方式而不是用 dependencies 參數方式,相比於 dependencies 參數方式,@Subcomponent 方式不須要父組件顯式顯露對象,就能夠拿到父組件所有對象,只須要在父 Component 接口中聲明拿這個 Subcomponent 的方法便可:
@Component(dependencies = AppComponent.class, modules = ActModule.class)
public interface ActivityComponent {
void inject(DependenceTestActivity DependenceTestActivity);
void inject(SubComponentTestActivity subComponentTestActivity);
//包含SubComponent,這樣的話該SubComponent也能夠拿到ActivityComponent中能提供的依賴。
ActSubComponent getActSubComponent();
}
複製代碼
@Subcomponent
public interface ActSubComponent {
void inject(SubFragment subFragment);
}
複製代碼
在獲取它的地方經過以下方式獲取便可:
mActivityComponent = DaggerActivityComponent
.builder()
.appComponent(((App) getApplication()).getAppComponent())
.actModule(new ActModule())
.build();
//獲取subcomponent
getActivityComponent().getActSubComponent().inject(this);
複製代碼
**具體關於 @Scope、@Subcomponent 的使用可見:juejin.im/entry/581c5… ,上面 Subcomponent 例子來自於此處 **
懶加載 - Lazy:
首先先改寫上面的例子
public class MainActivity extends AppCompatActivity {
@Inject
Lazy<Car> mCar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MainComponent component = DaggerMainComponent.create();
component.inject(this);
Car car = mCar.get();
Log.i(MainActivity.class.getName(), car.toString());
}
}
複製代碼
懶加載很好理解,就是 Component 初始化了一個對象,並將這個對象放在一個地方,啥時候要用的時候纔去 get,若是是第一次去調用它的 get 方法,它會進行實例化,然後面的調用就會直接獲取到第一次 get 進行實例化的那個實例了。這樣作能夠提升加載速度,好比在 AOSP 中的 SystemUI 項目,其依賴管理就是利用 Dagger2 的 Lazy 注入進行加載和獲取的(代碼位於 frameworks/base/packages/SystemUI/src/com/android/systemui/Dependency.java)。
強制加載 - Provider:
有時候不只僅是注入單個實例,而是須要多個實例,那麼就能夠用 Provider 注入了:
public class MainActivity extends AppCompatActivity {
@Inject
Provider<Car> mCar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MainComponent component = DaggerMainComponent.create();
component.inject(this);
for (int i = 0; i < 10; i++) {
Car car = mCar.get();
Log.i(MainActivity.class.getName(), car.toString());
}
}
}
複製代碼
使用 Provider 注入的對象,每次 get 都會新實例化一個實例出來,而不像 Lazy 那樣都是單例。
若是要注入一個接口的實現,通常咱們會使用以下方式:
@Provides
public XXInterface providesXX(XXImp imp) {
return imp;
}
複製代碼
但若是用 @Bind,那麼就能省下很多代碼:
@Binds
public abstract XXInterface bindXX(XXImp imp);
複製代碼
須要注意的是,這個 Module 也須要爲 abstract。
Dagger2 是一個對於大型項目來講很是好的依賴注入組件,其經過編譯時去生成輔助代碼進行依賴注入,可以減小代碼量,下降耦合,同時解耦對單元測試是很是友好的,可以提升應用的質量。可是若是是中小型項目我我的認爲使用 Dagger2 是沒有必要的,這點見仁見智。最初我看到簡單使用的時候以爲這個就是多餘的,但直到經過了解其餘相關的內容以及結合 AOSP 中 SystemUI 的實踐,我也慢慢體會到了 Dagger2 的一些魅力。
我的能力有限,如有不合理之處,但願你們可以指出。聯繫我:me@york1996.com
參考連接彙總: