Dagger2 是一個Android依賴注入框架,由谷歌開發(fā),最早的版本Dagger1 由Square公司開發(fā)。依賴注入框架主要用于模塊間解耦,提高代碼的健壯性和可維護性。
Dragger2 通過注解來生成代碼,定義不同的角色,主要的注解有:@Inject、@Module、@Component、@Provides、@Scope、@SubComponent等。
- @Inject:通常在需要依賴的地方使用這個注解。換句話說,你用它告訴Dragger這個類或者字段需要依賴注入。這樣,Dragger就會構(gòu)造一個這個類的實例并滿足他們的依賴。
- @Module: Modules類里面的方法專門提供依賴,所以我們定義一個類,用@Module注解,這樣Dragger在構(gòu)造類的實例的時候,就知道從哪里去找到需要的依賴。modules一個重要特性是他們設(shè)計為分區(qū)并組合在一起(比如說,在我們的app中可以有多個組成在一起的modules)
- @Provides:在modules中,我們定義的方法使用這個注入器,以此來告訴Dragger我們想要構(gòu)造對象并提供這些依賴。
- @Component: Components從根本上來說就是一個注入器,也可以說是@Inject和@Module的橋梁,它的主要作用就是連接這兩部分。Commponents可以提供所有定義了的類型的實例。比如:我們必須用@Commponent注解一個接口然后列出所有的@Modules組成該組件,如果缺失了任何一塊都會在編譯的時候報錯。所有組件都可以通過它的modules知道以來的范圍。
- @Scope:Dragger2可以通過自定義注解限定注解作用域。一般說來每一個Component都有一個自己的作用域
- @Qualifier: 當類的類型不足以鑒別一個依賴的時候,我們就可以使用這個注解標示。例如:在Android中,我們會需要不同類型的context,所以我們就可以定義qulifier注解"@perApp"和"@perActivity:,這樣當注入一個context的時候,我們就可以告訴Dagger我們想要那哪種類型的context。
結(jié)構(gòu)
Dagger2要實現(xiàn)一個完整的依賴注入,必不可少的元素有三種:Module,Component和Container。

為了便于理解,其實可以把component想象成針管,module是注射瓶,里面的依賴對象是注入的藥水,build方法是插進患者(Container),inject方法的調(diào)用是推動活塞。
一個簡單的例子
實現(xiàn)module
@Module // 注明本類是Module
public class MyModule{
@Provides // 注明該方法是用來提供依賴對象的方法
public B provideB(){
return new B();
}
}
實現(xiàn)Component
@Component(modules={ MyModule.class}) // 指明Component查找Module的位置
public interface MyComponent{ // 必須定義為接口,Dagger2框架將自動生成Component的實現(xiàn)類,對應(yīng)的類名是Dagger×××××,這里對應(yīng)的實現(xiàn)類是DaggerMyComponent
void inject(A a); // 注入到A(Container)的方法,方法名一般使用inject
}
實現(xiàn)Container
A就是可以被注入依賴關(guān)系的容器
public A{
@Inject //標記b將被注入
B b; // 成員變量要求是包級可見,也就是說@Inject不可以標記為private類型。
public void init(){
DaggerMyComponent.create().inject(this); // 將實現(xiàn)類注入
}
}
當調(diào)用A的init()方法時,b類自動被賦予實現(xiàn)類的對象。
更多用法
方法參數(shù)
上面的例子@Provdes標注的方法是沒有輸入?yún)?shù)的,Module中@Provides標注的方法是可以帶輸入?yún)?shù)的,其參數(shù)值是可以由Module中其它被@Provides標注的方法提供。
@Module
public class MyModule{
@Provides
public B provideB(C c){
return new B(c);
}
@Provides
pulic C provideC(){
return new C();
}
}
如果找不到到@Provides注釋的方法提供對應(yīng)參數(shù)對象的話,將自動調(diào)用被@Inject注釋的構(gòu)造方法生成相應(yīng)對象。
@Module
public class MyModule{
@Provides
public B provideB(C c){
return new B(c);
}
}
public class C{
@Inject
Public C(){
}
}
添加多個Module
一個Commponent可以添加多個Module,這樣Component獲取依賴的時候會自動從多個Module中查找獲取。添加多個Module有兩種方法,一種就是在Component的注解@Component(modules={××××,×××})中添加多個modules
@Component(modules={ModuleA.class,ModuleB.class,ModuleC.class})
public interface MyComponent{
...
}
另外一種添加多個Module的方法可以使用@Module的 includes的方法(includes={××××,×××})
@Module(includes={ModuleA.class,ModuleB.class,ModuleC.class})
public class MyModule{
...
}
@Component(modules={MyModule.class})
public interface MyComponent{
...
}
創(chuàng)建Module實例
上面簡單例子中,當調(diào)用DaggerMyComponent.create()實際上等價于調(diào)用了DaggerMYComponent.builder().build().可以看出DaggerMyComponent使用了Builder構(gòu)造者模式。在構(gòu)建的過程中,默認使用Module無參構(gòu)造器產(chǎn)生實例。如果需要傳入特點的Module實例??梢允褂?/p>
DaggerMyComponent.builder()
.moduleA(new ModuleA())
.moduleB(new ModuleB())
.build()
區(qū)分@Provides方法
這里以Android Context為例。當有Context需要注入時,Dagger2就會在Module中查找返回類型為Context的方法。但是,當Container需要依賴兩種不同的Context的時候,你就需要寫兩個@Provides方法,并且這兩個@Provides方法都是返回Context類型,靠跑別返回值得做法就行不通了。這就可以使用@Named注解來區(qū)分
//定義Module
@Module
public class ActivityModule{
private Context mContext ;
private Context mAppContext = App.getAppContext();
public ActivityModule(Context context) {
mContext = context;
}
@Named("Activity")
@Provides
public Context provideContext(){
return mContext;
}
@Named("Application")
@Provides
public Context provideApplicationContext (){
return mAppContext;
}
}
//定義Component
@Component(modules={ActivityModule.class})
interface ActivityComponent{
void inject(Container container);
}
//定義Container
class Container extends Fragment{
@Named("Activity")
@Inject
Context mContext;
@Named("Application")
@Inject
Context mAppContext;
...
public void init(){
DaggerActivityComponent.
.activityModule(new ActivityModule(getActivity()))
.inject(this);
}
}
這樣,只有相同的@Named的@Inject成員變量與@Provides方法才可以被對應(yīng)起來。
**更常用的方法是使用@Qualifier來自定義注解
@Qualifier
@Documented //起到文檔提示作用
@Retention(RetentionPolicy.RUNTIME) //注意注解范圍是Runtime級別
public @interface ContextLife {
String value() default "Application"; // 默認值是"Application"
}
//定義Module
@Module
public class ActivityModule{
private Context mContext ;
private Context mAppContext = App.getAppContext();
public ActivityModule(Context context) {
mContext = context;
}
@ContextLife("Activity")
@Provides
public Context provideContext(){
return mContext;
}
@ ContextLife ("Application")
@Provides
public Context provideApplicationContext (){
return mAppContext;
}
}
//定義Component
@Component(modules={ActivityModule.class})
interface ActivityComponent{
void inject(Container container);
}
//定義Container
class Container extends Fragment{
@ContextLife ("Activity")
@Inject
Context mContext;
@ContextLife ("Application")
@Inject
Context mAppContext;
...
public void init(){
DaggerActivityComponent.
.activityModule(new ActivityModule(getActivity()))
.inject(this);
}
}
組件依賴
假設(shè)ActivityComponent依賴ApplicationComponent。當使用ActivityComponent注入Container的時候,如果找不到對應(yīng)的依賴,就會到ApplicationCoponent中去查找。但是ApplicationComponent必須顯式把ActivityComponent找不到的依賴提供給ActivityComponent。
//定義ApplicationModule
@Module
public class ApplicationModule {
private App mApplication;
public ApplicationModule(App application) {
mApplication = application;
}
@Provides
@ContextLife("Application")
public Context provideApplicationContext() {
return mApplication.getApplicationContext();
}
}
//定義ApplicationComponent
@Component(modules={ApplicationModule.class})
interface ApplicationComponent{
@ContextLife("Application")
Context getApplication(); // 對外提供ContextLife類型為"Application"的Context
}
//定義ActivityComponent
@Component(dependencies=ApplicationComponent.class, modules=ActivityModule.class)
interface ActivityComponent{
...
}
進階用法
單利用法
創(chuàng)建某些對象有時候是耗時、浪費資源的或者需要確保其唯一性,這時就需要使用@Singleton注解標注為單利。
@Module
class MyModule{
@Singleton // 標明該方法只產(chǎn)生一個實例
@Provides
B provideB(){
return new B();
}
}
@Singleton // 標明該Component中有Module使用了@Singleton
@Component(modules=MyModule.class)
class MyComponent{
void inject(Container container)
}
※注意:Java中,單例通常保存在一個靜態(tài)域中,這樣的單例往往要等到虛擬機關(guān)閉時候,該單例所占用的資源才釋放。但是,Dagger通過注解創(chuàng)建出來的單例并不保持在靜態(tài)域上,而是保留在Component實例中。所以說不同的Component實例提供的對象是不同的。
自定義Scope
@Singleton就是一種Scope注解,也是Dagger2唯一自帶的Scope注解,下面是@Singleton的源碼
@Scope
@Documented
@Retention(RUNTIME)
public @interface Singleton{}
可以看到定義一個Scope注解,必須添加以下三部分:
@Scope :注明是Scope
@Documented :標記文檔提示
@Retention(RUNTIME) :運行時級別
對于Android,我們通常會定義一個針對整個App全生命周期的@PerApp的Scope注解和針對一個Activity生命周期的@PerActivity注解,如下:
@Scope
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface PerApp {
}
@Scope
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface PerActivity {
}
@PerApp的使用例:
@Module
public class ApplicationModule {
private App mApplication;
public ApplicationModule(App application) {
mApplication = application;
}
@Provides
@PerApp
@ContextLife("Application")
public Context provideApplicationContext() {
return mApplication.getApplicationContext();
}
}
@PerApp
@Component(modules = ApplicationModule.class)
public interface ApplicationComponent {
@ContextLife("Application")
Context getApplication();
}
// 單例的有效范圍是整個Application
public class App extends Application {
private static ApplicationComponent mApplicationComponent;
public void onCreate() {
mApplicationComponent = DaggerApplicationComponent.builder()
.applicationModule(new ApplicationModule(this))
.build();
}
// 對外提供ApplicationComponent
public static ApplicationComponent getApplicationComponent() {
return mApplicationComponent;
}
}
@PerActivity的使用例子:
public abstract class BaseActivity extends AppCompatActivity {
protected ActivityComponent mActivityComponent;
// 對外提供ActivityComponent
public ActivityComponent getActivityComponent() {
return mActivityComponent;
}
public void onCreate() {
mActivityComponent = DaggerActivityComponent.builder()
.applicationComponent(App.getApplicationComponent())
.activityModule(new ActivityModule(this))
.build();
}
}
通過上面的例子可以發(fā)現(xiàn),使用自定義Scope可以很容易區(qū)分單例的有效范圍。
子組件
可以使用@Subcomponent注解拓展原有component。Subcomponent其功能效果優(yōu)點類似component的dependencies。但是使用@Subcomponent不需要在父component中顯式添加子component需要用到的對象,只需要添加返回子Component的方法即可,子Component能自動在父Component中查找缺失的依賴
//父Component:
@Component(modules=××××)
public AppComponent{
SubComponent subComponent (); // 這里返回子Component
}
//子Component:
@Subcomponent(modules=××××)
public SubComponent{
void inject(SomeActivity activity);
}
// 使用子Component
public class SomeActivity extends Activity{
public void onCreate(Bundle savedInstanceState){
App.getComponent().subCpmponent().inject(this); // 這里調(diào)用子Component
}
}
懶加載
可以使用Lazy來包裝Container中需要被注入的類型為延遲加載
public class Container{
@Inject Lazy<B> b;
public void init(){
DaggerComponent.create().inject(this);
B b=b.get(); //調(diào)用get時才創(chuàng)建b
}
}
另外可以使用Provider實現(xiàn)強制加載,每次調(diào)用get都會調(diào)用Module的Provides方法一次,和懶加載模式正好相反。