命名規約
- @Provides方法用provide前綴命名
- @Module 用Module后綴命名
- @Component 以Component作為后綴
簡單的說,就是一個工廠模式,由Dagger負責創建工廠,幫忙生產instance。遵從Java規范JSR 330,可以使用這些注解。現在不研究Dagger2是如何根據注解去生成工廠的,先來看看工廠是什么東西,理解為什么可以實現了DI(Dependency Injection),如何創建IoC(Inverse of Control)容器。
- Dagger2是通過依賴注入完成類的初始化。
這個過程需要三部分:
#1 依賴提供方(生產者)
#2 依賴注入容器(橋梁)
#3 依賴需求方(消費者)
image.png
Dagger2是怎么選擇依賴提供的呢,規則是這樣的:
步驟1:查找
Module
中是否存在創建該類的方法。
步驟2:若存在創建類方法,查看該方法是否存在參數
步驟2.1:若存在參數,則按從步驟1開始依次初始化每個參數
步驟2.2:若不存在參數,則直接初始化該類實例,一次依賴注入到此結束
步驟3:若不存在創建類方法,則查找Inject
注解的構造函數,看構造函數是否存在參數
步驟3.1:若存在參數,則從步驟1開始依次初始化每個參數
步驟3.2:若不存在參數,則直接初始化該類實例,一次依賴注入到此結束
- 在使用@Component的時候必須要提供scope范圍,標準范圍是@Singleton
- @Component在使用@Module的時候必須匹配相同的scope
- 能使用Singleton的時候,要注意標注,否則默認多例
總結:
-
@Inject
主要有兩個作用
#1 作為依賴注提供方:
使用@Inject注解構造方法。
注解類的構造函數,讓Dagger2
幫我們實例化該類,并注入。
#2 作為依賴需求方:
使用@Inject注解成員。
如果一個成員變量被@Inject
注解修飾,并且成員類的構造函數也被@Inject
注解,那么dagger2
幫我們實例化該成員類,并注入。
通常在需要依賴的地方使用這個注解。換句話說,你用它告訴Dagger這個類或者字段需要依賴注入。這樣,Dagger就會構造一個這個類的實例并滿足他們的依賴。
使用@Inject可以讓IoC容器負責生成instance,如果沒有這個注解,dagger將不認識,當做普通類,無法代理
-
@Module
#1@Module
注解類,負責管理依賴。
Module 其實是一個簡單工廠模式,Module 里面的方法都是創建相應類實例的方法。
#2 通過@Module
獲得第三方類庫的對象。
#3@Module
是一個依賴提供方的合集。
@Module
public class AModule {
@Provides
public Gson provideGson(){
return new Gson();
}
}
@Provides
#1 注解@Module
類中的方法。
在modules中,我們定義的方法是用這個注解,以此來告訴Dagger我們想要構造對象并提供這些依賴。@Component
#1@Component
一般用來注解接口。
#2 負責在@Inject
和@Module
之間建立連接。
也可以說是@Inject和@Module的橋梁,它的主要作用就是連接這兩個部分。
#3 實例化@Inject
注解的類時,遇到沒有構造函數的類依賴,則該依賴由@Module
修飾的類提供。
#4 依賴注入容器只是一個接口interface
。
Component需要引用到目標類的實例,Component會查找目標類中用Inject注解標注的屬性,查找到相應的屬性后會接著查找該屬性對應的用Inject標注的構造函數(這時候就發生聯系了),剩下的工作就是初始化該屬性的實例并把實例進行賦值。因此我們也可以給Component叫另外一個名字注入器(Injector)
Component注解的類,再編譯之后,會生產一個以Dagger+類名的一個類,如下面的MainComponent會生成類DaggerMainComponent(補充一點,Kotlinkapt編譯生成類的位置:\build\generated\source\kapt\debug),我們需要在目標類MainActivity中加入下面代碼
DaggerMainComponent.builder()
.build()
.inject(this)
DaggerMainComponent使用了建造者設計模式,inject方法是我們MainComponent中定義的,這樣目標類就和Component建立了聯系.Component會去遍歷使用@Inject注解的常量,然后去查找對應的類是否有@Inject注解的構造方法,如果沒有就會報異常.
@Component {modules = {HeaterModule.class, PumperModule.class}}
public interface MachineComponent {
void inject(CoffeeMachine machine);
}
dagger中Component就是最頂級的入口,dagger為之生成了工廠類 DaggerMachineComponent,目標是構建CoffeeMachine, 在CoffeeMachine中使用了Injection,那么依賴要由工廠類來提供。工廠類是根據modules的參數來找依賴綁定的。
本例中,指向了HeaterModule, PumperModule,意思是CoffeeMachine的依賴要從這些module里找。
工廠名稱生成規則
如果Component是接口, 則生成Dagger+接口名
如果Component是內部接口,比如本例,則生成Dagger+類名+ _+ 接口名
@Scope
Scopes可是非常的有用,Dagger2可以通過自定義注解限定注解作用域。后面會演示一個例子,這是一個非常強大的特點,因為就如前面說的一樣,沒 必要讓每個對象都去了解如何管理他們的實例。在scope的例子中,我們用自定義的@PerActivity注解一個類,所以這個對象存活時間就和 activity的一樣。簡單來說就是我們可以定義所有范圍的粒度(@PerFragment, @PerUser, 等等)。Qualifier
當類的類型不足以鑒別一個依賴的時候,我們就可以使用這個注解標示。例如:在Android中,我們會需要不同類型的context,所以我們就可以定義 qualifier注解“@ForApplication”和“@ForActivity”,這樣當注入一個context的時候,我們就可以告訴 Dagger我們想要哪種類型的context。
/**
* View層,負責界面的展示
*/
public class TestActivity extends AppCompatActivity implements IView{
//當一個成員變量被@Inject注解修飾,并且它的類型構造函數也被@Inject注解修飾,dagger2就會自動實例化該成員類型,并注入到該成員變量
@Inject
TestPresent mPresent;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
DaggerTestComponent.builder().testModule(new TestModule(this)).build().inject(this);//@Component負責連接起@Inject和@Module注解
mPresent.updateUI();
}
@Override
public void updateUI(String text) {
((TextView)findViewById(R.id.textview)).setText(text);
}
}
/**
* Present類,調用Model層的業務方法,更新View層的界面展示
*/
public class TestPresent {
IView mView;
@Inject
TestModel mModel;//Dagger2遇到@Inject標記的成員屬性,就會去查看該成員類的構造函數,如果構造函數也被@Inject標記,則會自動初始化,完成依賴注入。
//TestPresent的構造函數也被@Inject注解修飾
@Inject
public TestPresent(IView view){
this.mView=view;
}
public void updateUI(){
mView.updateUI(mModel.getText());
}
}
/**
* Model類,實現具體的業務邏輯
*/
public class TestModel {
//構造函數用@Inject修飾
@Inject
public TestModel(){
}
public String getText(){
return "Dagger2應用實踐...";
}
}
/**
* Module類提供那些沒有構造函數的類的依賴,如第三方類庫,系統類,接口類
*/
@Module
public class TestModule {
private IView mView;
public TestModule(IView iView){
this.mView=iView;
}
//@Provides注解的方法,提供IView類的依賴。
@Provides
public IView provideIView(){
return this.mView;
}
}
/**
*Component必須是一個接口類或者抽象
*/
@Component(modules = TestModule.class)
public interface TestComponent {
void inject(TestActivity testActivity);
}