NOTE1 : Dagger2的官網地址: https://google.github.io/dagger/
NOTE2 : 使用前請先了解JAVA中的註解基本知識,如果對實現原理感興趣可以瞭解下APT技術(不瞭解也不影響正常使用)
NOTE3: 本文所涉及的示例已上傳至Github:
https://github.com/bailandi/Dagger2Demo
在具體使用前,首先來了解一個概念——依賴注入
依賴注入是由於類之間的依賴關係產生的,比如:
//產品依賴於操作工
public class Product{
private Worker mWorker;
}
這個時候想要產生mWorker
的實例,通常有兩種方法:
//直接new
mWorker = new Worker();
//通過參數傳入
public Product(Worker worker){
mWokrer = worker;
}
以上就是典型的依賴注入,那麼爲什麼要使用Dagger2來進行依賴注入呢?
在回答這個問題前,我們首先分析以上兩種產生mWorker
實例的方式存在什麼問題。
- 對於第一種方式,假設當業務擴充,此時
Worker
的構造需要依賴於工具小刀Knife
,Worker
的構造函數發生變化,我們不得不對Product
做出修改,這嚴重違反了開閉原則。 - 對於第二種方式,假設工廠
Factory
依賴於Product
,此時Product
的位置就會與第一種的Worker
一樣。我們可以從迪米特原則(最少知識原則)——一個類應該對自己直接耦合(依賴)的類知道最少中找到這一問題產生的根本原因,顯然,Factory
並不關心Worker
,然而Product
卻在構造時將對Wokrer
的依賴帶入到了Factory
中
爲了解決這種依賴關係對業務擴展時造成的糟糕體驗,我們使用Dagger2來進行依賴注入
一切實踐從問題出發,首先我們來解決第一個問題,
1. 如何使用Dagger2注入一個簡單對象到目標類中
讓我們先複製一個示例,它包含下面三部分
@Module
public class MainModule {
@Provides
public Product provideProduct() {
return new Product();
}
}
@Component(modules = {MainModule.class})
public interface MainComponent {
void inject(MainActivity activity);
}
public class MainActivity extends AppCompatActivity {
@Inject
Product mProduct;
@Override
protected void onCreate(Bundle savedInstanceState) {
//...省略無關代碼
DaggerMainComponent
.builder()
.build()
.inject(this);
mProduct.create();
}
}
我們依次對三個部分所涉及的內容進行解釋:
1.1 @Module
@Module
註解用於獲取對象實例的類,Dagger2根據該註解知道應該去哪個類裏獲取對象實例
1.2 @Provides
@Provides
註解用於module類中獲取對象實例的方法,Dagger2根據該註解及方法的返回值類型將對象實例注入到對應的引用中
1.3 @Inject
@Inject
用於註解構造函數或成員變量
當作用於成員變量時,Dagger2根據該註解及成員變量的類型,從moudle中得到相應實例。
注意:成員變量的訪問修飾符不能是private-
當作用於構造函數時,就是Dagger2對於獲取對象實例的第二種方式,比如上面的例子其實可以直接在
Product
的構造函數上註解@Inject
,並移除@Provides
註解的方法@Inject public Product() { }
但是
@Inject
並非適用於所有的地方,比如以下三種情況:- 不具有構造函數的接口
- 遠程引入的三方庫中的類無法被添加註解
- 通過建造者模式等方式可配置化的進行構造的對象
而使用
@Provides
可以更好的處理這些問題
1.4 @Component
@Component
註解用於擔任連接橋樑的接口,其兩端分別是在@Component的參數中指定的modules數組
和在方法參數中指定的具體類
注意:方法參數必須是要注入的具體類,而非其父類或接口
1.5 DaggerMainComponent
在創建Module和Component後,我們需要build ——> Make Project
,這樣APT會在指定目錄下生成對應的java文件,其中就會包含名如DaggerXX
的的文件,它實現了我們所創建的Component接口,最終要完成注入,只需要像這樣調用:
DaggerMainComponent
.builder()
.build()
.inject(this);
2. 簡單分析自動生成的代碼
2.1 DaggerXX類
public final class DaggerMainComponent implements MainComponent {
private final MainModule mainModule;
private DaggerMainComponent(MainModule mainModuleParam) {
this.mainModule = mainModuleParam;
}
public static Builder builder() {
return new Builder();
}
public static MainComponent create() {
return new Builder().build();
}
@Override
public void inject(MainActivity activity) {
injectMainActivity(activity);
}
private MainActivity injectMainActivity(MainActivity instance) {
MainActivity_MembersInjector.injectMProduct(
instance, MainModule_ProvideProductFactory.provideProduct(mainModule));
return instance;
}
public static final class Builder {
private MainModule mainModule;
private Builder() {}
public Builder mainModule(MainModule mainModule) {
this.mainModule = Preconditions.checkNotNull(mainModule);
return this;
}
public mainComponent build() {
if (mainModule == null) {
this.mainModule = new mainModule();
}
return new DaggerMainComponent(mainModule);
}
}
}
它實現了我們所創建的Component接口,內部使用了建造者模式
,方便根據不同需求構建該對象實例,比如我們可以傳入module實例替換默認的無參實例,關鍵看一下我們定義的inject
方法的具體實現。
2.2 MainActivity_MembersInjector類和MainModule_ProvideProductFactory類
public final class MainActivity_MembersInjector implements MembersInjector<MainActivity> {
//...省略無關代碼
public static void injectMProduct(MainActivity instance, Product mProduct) {
instance.mProduct = mProduct;
}
}
public final class MainModule_ProvideProductFactory implements Factory<Product> {
//...省略無關代碼
public static Product provideProduct(MainModule instance) {
return Preconditions.checkNotNull(
instance.provideProduct(), "Cannot return null from a non-@Nullable @Provides method");
}
}
其實關鍵的方法就是injectMProduct()
和provideProduct()
,一看就懂,沒什麼可細說的。
3. 含有參數(依賴關係)的構造
當Product
必須與Worker
關聯時,有如下構造函數
public Product(Worker worker) {
mWorker = worker;
}
此時我們提供Product
實例對象時需要作出以下修改
@Provides
public Product provideProduct(Worker worker) {
return new Product(worker);
}
即我們需要傳入Worker
來構建Product,那麼Worker
實例如何提供呢?其實同樣的,我們可以使用簡單構造時的兩種方式提供Worker
實例,具體就不在贅述,可以自行實踐。
下一篇
,我們將對問題進行升級,並瞭解@Scope、@Qualifier、@binds、dependencies、Lazy的使用