Dagger2 入門 1
這篇文章主要談一下本人在學習Dagger2的心得,如有錯漏,敬請諒解。
什么是依賴注入
依賴注入就是把下面這樣的代碼:
class A {
public A() {
}
class B {
A a;
public B() {
a = new A();
}
}
class C {
A a;
B b;
public C() {
a = new A();
b = new B();
b.a = a;
}
}
main() {
C c = new C();
}
變成:
class A {
A() {
}
}
class B {
A a;
B(A a) {
this.a = a;
}
}
class C {
A a;
B b;
C(A a, B b) {
this.a = a;
this.b = b;
}
}
main() {
A a = new A();
B b = new B(a);
C c = new C(a, b);
}
這種把對象之間的依賴生成的責任交給外界的做法,叫做依賴注入。
如何更方便地進行依賴注入
我們有類和它們之間的依賴關系,便很自然地會用圖來表示這種狀態。如上例子所示,可用下面這樣一個圖來表示:
+-----+
| |
+----> | A | <----+
| | | |
| +-----+ |
| |
| |
+---+---+ +---+---+
| | | |
| B | <---------+ C |
| | | |
+-------+ +-------+
箭頭表示依賴的對象。
我們想要這樣的一種依賴注入框架:當我們需要一個B對象時,框架按照依賴遍歷這個圖,生成A,然后將其注入B,最后返回一個已經生成好的B對象。大概是:
B b = Injector.create(B.class)
另外,如果要求A對象是單例(這里不解釋什么是單例)或對象的生成符合某種指定的規則,框架應自動識別并作出處理。
設計框架
我們面對兩個主要問題:如何表示依賴圖和如何生成對象。
依賴圖的表示
我們需定義一種聲明依賴的方法。可以用xml,json,甚至DSL來完成這個任務。這里我們采用比較流行和簡便的注解(annotation)來表示依賴關系。
假設我們要的效果如下所示:
@dependence
class A {
}
@dependence(A.class)
class B {
}
@dependence({A.class, B.class})
class C {
}
可以看到,我們用@dependence注解來表示上面例圖中的箭頭。各個類之間的依賴關系十分清晰。
如果要求A是單例,我們可以這樣:
@singleton
@dependence()
class A {
}
對象生成
建立了依賴圖以后,需要通過某種方式生成我們需要的對象。我們希望是這樣的:
B b = Injector.create(B.class)
或者通過注解實現自動注入
class Main {
@Inject
B b;
main() {
Injector.inject(this);
}
}
Dagger2
我們來看一下Dagger2是如何實現上述兩個目標的。
依賴圖的表示
Dagger2中,是通過@Inject注解或者@Module和@Provide這兩個注解建立依賴圖,如下所示:
首先定義好類:
public class A {
public A(){
}
}
public class B {
A a;
public B(A a) {
this.a = a;
}
}
public class C {
A a;
B b;
public C(A a, B b) {
this.a = a;
this.b = b;
}
}
然后我們用第一種方法來聲明依賴:
public class A {
@Inject
public A() {
}
}
public class B {
A a;
@Inject
public B(A a) {
this.a = a;
}
}
public class C {
A a;
B b;
@Inject
public C(A a, B b) {
this.a = a;
this.b = b;
}
}
可以看到我們為每一個類的方法添加了@Inject聲明,表示該類是依賴圖中的一個節點。如果該初始化方法含有參數,那么這些從參數也應是依賴圖中的節點。
第二種方法是通過一個module類來聲明依賴,如下所示:
@Module
public class ABCModule {
@Provides
public A provideA() {
return new A();
}
@Provides
public B provideB(A a) {
return new B(a);
}
@Provides
public C provideC(A a, B b) {
return new C(a, b);
}
}
@Module注解表示這個ABCModule的作用是聲明“依賴圖”的。@Provides注解表示當前方法的返回值是圖中的一個節點,方法的參數是依賴的對象,即前文中箭頭指向的目標。
再強調一次,Dagger要求圖中的每一個節點都要聲明,即每一個節點都要在module中有@Provides注解的方法或者@Inject注解的初始化方法。
可以看到第二種方式(module)無需修改原來的對象。為了讓模塊盡量少地依賴第三方庫,一般采用第二種方式來聲明依賴圖。
對象生成
Dagger2中,從依賴圖中獲取對象需通過component。component是依賴圖和被注入對象之間的橋梁。如下所示:
@Component(module=ABCModule.class)
public interface ABCComponent {
public A provideA();
public B provideB();
public C provideC();
void inject(ActivitymainActivity);
}
@Component注解表示ABCComponent這個接口是一個Component。Component的方法隱含著如下兩條規則:
-
不帶參數的方法為“provider”方法,該方法的返回值是從依賴圖中取得的對象。如下所示(偽代碼):
class Main {
C c; public void init() { c = Component.provideC(); }
}
-
帶參數的方法,參數為“注入對象”。通常于@Inject標簽同時使用。如下所示(偽代碼):
class Main {
@Inject C c; public void init() { Component.inject(this); }
}
即調用Component.inject(foorbar)的時候,框架自動為用@Inject標簽標注的屬性注入依賴。要求@Inject的屬性的類必須是依賴圖中的節點。
注意:component的方法必需至少符合以上兩條規則中的一條。
注意:provider方法的名字一般為“provider ”,inject方法的名字一般為“inject ”,但名字不影響這兩個方法的功能。
當Component聲明好以后,框架會在編譯時生成一個Dagger Component名字 的類,我們可以用它來實施依賴注入,如下所示:
ABCComponentabcComponent = DaggerABCComponent.create();
A a = abcComponent.provideA();
B b = abcComponent.provideB();
C c = abcComponent.provideC();
或者:
class Main {
@Inject
A a;
@Inject
B b;
@Inject
C c;
public static void main() {
ABCComponentabcComponent = DaggerABCComponent.create();
abcComponent.inject(this);
}
}
Component標簽的module屬性可以是一個數組,即一個Component實施多個module的注入。引入類D和DModule:
class D {
public D() {
}
}
@Module
public class DModule {
@Provides
public D provideD() {
return new D();
}
}
修改ABCComponent,如下:
@Component(module={ABCModule.class, DModule.class})
public interface ABCComponent {
public A provideA();
public B provideB();
public C provideC();
public D provideD();
void inject(ActivitymainActivity);
}
如上即可實現D對象的注入。
Component之間的依賴
真正實施工程的時候,會將對象以功能分類。例如network相關,DB相關,Util相關的類集中在一起管理。Dagger2為方便我們達到這一個目的,在component中引入了dependence這個功能。
例如我們有如下component
@Component(modules = DModule.class)
public interface DComponent {
D provideD();
}
假設DComponent負責提供一個對象D。這種能力是項目無關的,我們把這個Component獨立出來。然后我們可以通過@Component的dependence屬性來為其他Component引入DComponent的能力。例如:
@Component(modules = ABCModule.class, dependencies = DComponent.class)
public interface ABCComponent {
A provideA();
B provideB();
C provideC();
D provideD();
void inject(Mainmain);
}
可以看到,聲明了dependencies=DComponent.class以后,provideD方法可以順利拿到D對象。inject方法也可以注入D對象。
public class Main {
@Inject
D d; // inject D by ABCComponent
public Main() {
DComponentdComponent = DaggerDComponent.create();
D d1 = dComponent.provideD(); // inject D by DComponent
ABCComponentabcComponent = DaggerABCComponent
.builder()
.dComponent(dComponent)
.build();
D d2 = abcComponent.provideD();
abcComponent.inject(this);
}
}
DComponent不知道ABCComponent的存在,故可以像普通Component那樣子使用。但在使用ABCComponent時,我們需要顯式地為ABCComponent注入DComponent對象:
ABCComponentabcComponent = DaggerABCComponent
.builder()
.dComponent(dComponent)
.build();
@Singleton
如上面例子所示,如果要求D對象為單例,可以通過@Singleton注解來實現。首先我們需要在依賴圖中聲明對象是單例的:
@Module
public class DModule {
@Provides
@Singleton
public D provideD() {
return new D();
}
}
DComponent接口也需要聲明:
@Singleton
@Component(modules = DModule.class)
public interface DComponent {
D provideD();
}
如此,當我們注入D對象時,可保證每次注入的是同一個D對象:
DComponentdComponent = DaggerDComponent.create();
D d1 = dComponent.provideD();
D d2 = dComponent.provideD();
// d1 == d2
總結
這篇文章只是簡單地介紹了Dagger2的基本用法,下一篇打算講Dagger2中的Scope和Subcomponent還在一些別的東西,敬請期待。
來自:http://legendmohe.net/2016/08/20/android-dagger2-入門-1/