最近這幾天一直在看有關Dagger2
有關的文章,感受就是這東西真難用、真難懂,數次想要放棄,還好有網上大神的教程幫助,模模糊糊總算能把基本的幾個概念跑通了。html
這裏首先推薦 牛曉偉 的下面一系列文章,其將Dagger2
的基本思想講的通俗易懂:java
接下來是結合具體例子進行分析的兩篇文章,適合在理解基本思想的前提下對Dagger2
有個更直觀的認識:android
還有前同事 Johnny Shieh 的Dagger2
系列,裏面提到了後來新增的一些用法:git
瞭解完以上這些基礎文章,就能夠嘗試去啃一下官方的英文文檔了,裏面介紹瞭如何在Android
中使用Dagger2
,可是不得不說,寫的真是晦澀難懂,網上找了好久,也沒有把AndroidInjector
說明白的:github
雖然網上的例子不少,可是咱們花了那麼多時間去看這些文章,本質上仍是要用到項目中,這裏確定要優先推薦Google
的官方架構Demo
:todo-mvp-dagger2,這裏面涉及到了不少新的註解用法。網絡
在導入依賴的時候,須要考慮當前工程中使用的Gradle
插件的版本。若是當前的插件版本小於2.2
,那麼須要引入 android-apt 插件,Dagger2 入門,以初學者角度 中就是用的這種方式。架構
在示例代碼中,根目錄下的build.gradle
文件配置的Gradle
插件版本爲2.3.3
:app
2.2
,因此我只須要在
app
模塊中的
build.gradle
文件中,引入如下兩個依賴就行了:
下面,咱們用一個簡單的例子來演示使用Dagger2
完成依賴注入的兩種方式:在進行數據的讀寫時,咱們能夠經過數據管理者DataRepository
來管理數據源,外部經過調用這個數據倉庫提供的方法來讀寫數據,完整的源碼能夠查看 Dagger2Sample 中第一章。框架
對於這些數據源的賦值,就能夠採用依賴注入的方式來實現。ide
DataRepository
:目標類。DataRepository
內部的數據源:目標依賴類。咱們先看第一種方法,經過「在目標依賴類構造函數上使用@Inject
,來完成依賴注入」。
先建立一個LocalSource
表示本地的數據源,並在它構造函數上加上@Inject
註解。這樣Dagger2
在嘗試建立一個LocalSource
對象賦值給DataRepository
中的mLocalSource
變量時,就會調用這個構造函數。
public class LocalSource {
@Inject
public LocalSource() {}
public String getData() {
return "使用在構造函數上使用 @Inject 的方式,獲取到了本地數據";
}
}
複製代碼
接下來須要聲明一個用@Component
註解的接口或者抽象類,用於注入依賴,這個接口的方法名能夠爲任意值,可是其形參必須是目標類的具體類型,而返回值只能爲void
或者目標類的具體類型。
@Component
public interface SourceComponent {
public void inject(DataRepository dataRepository);
}
複製代碼
作好前期這些準備,接下來須要按順序進行如下幾步操做:
@Inject
註解。make
一下工程,讓Dagger2
根據SourceComponent
中聲明的接口建立一個DaggerSourceComponent
實現類。DaggerSourceComponent.create().inject(this)
方法,完成依賴注入。DataRepository
文件以下所示:
public class DataRepository {
@Inject
LocalSource mLocalSource;
public DataRepository() {
DaggerSourceComponent.create().inject(this);
}
public String getData() {
return mLocalSource.getData();
}
}
複製代碼
流程以下圖所示:
3.1
的實現方式有一個缺點,就是須要修改構造函數,可是這對於一些第三方的對象來講是不可能作到的,此時就須要經過另外一種方法來建立對象。
咱們經過下面這個例子來演示:在DataRepository
中實例化一個遠程數據源RemoteSource
。
在RemoteSource
的構造函數上再也不須要添加@Inject
註解:
public class RemoteSource {
public String getData() {
return "使用 @Module 的方式,獲取到了網絡數據";
}
}
複製代碼
接下來建立一個RemoteSourceModule
類,用於提供RemoteSource
對象。Dagger2
會根據它聲明的方法的返回值類型去識別提供的是哪一種類型的對象,這裏有兩點須要注意:
Module
類須要加上@Module
註解RemoteSource
的方法須要加上@Provides
註解@Module
public class RemoteSourceModule {
@Provides
public RemoteSource provideRemoteSource() {
return new RemoteSource();
}
}
複製代碼
在SourceComponent
中,咱們須要告訴它哪些Module
能夠用來建立目標類所依賴的實例,這裏和第一種方式的區別就是須要在@Component
後面加上用來建立依賴實例的Module
類名:
@Component(modules = {RemoteSourceModule.class})
public interface SourceComponent {
public void inject(DataRepository dataRepository);
}
複製代碼
而在目標類中,RemoteSource
和LocalSource
同樣,都須要加上@Inject
註解:
public class DataRepository {
@Inject
LocalSource mLocalSource;
@Inject
RemoteSource mRemoteSource;
public DataRepository() {
DaggerSourceComponent.create().inject(this);
}
public String getData() {
return mLocalSource.getData();
}
public String getNetData() {
return mRemoteSource.getData();
}
}
複製代碼
流程以下圖所示:
下面,咱們用一個簡單的程序來驗證DataRepository
中的mLocalSource/mRemoteSource
是否注入成功:
public class RepositoryActivity extends AppCompatActivity {
private static final String TAG = RepositoryActivity.class.getSimpleName();
private Button mBtnGetData;
private Button mBtnGetNetData;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_repository);
mBtnGetData = (Button) findViewById(R.id.bt_get_data);
mBtnGetData.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
DataRepository repository = new DataRepository();
String data = repository.getData();
Toast.makeText(RepositoryActivity.this, data, Toast.LENGTH_SHORT).show();
}
});
mBtnGetNetData = (Button) findViewById(R.id.bt_get_net_data);
mBtnGetNetData.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
DataRepository repository = new DataRepository();
String data = repository.getNetData();
Toast.makeText(RepositoryActivity.this, data, Toast.LENGTH_SHORT).show();
}
});
}
}
複製代碼
運行結果爲:
下面,咱們來看一下依賴注入的內部實現,整個依賴注入的入口函數爲DaggerSourceComponent
,在上面的例子中,它的源碼爲:
public final class DaggerSourceComponent implements SourceComponent {
private Provider<RemoteSource> provideRemoteSourceProvider;
private MembersInjector<DataRepository> dataRepositoryMembersInjector;
private DaggerSourceComponent(Builder builder) {
assert builder != null;
initialize(builder);
}
public static Builder builder() {
return new Builder();
}
public static SourceComponent create() {
return new Builder().build();
}
@SuppressWarnings("unchecked")
private void initialize(final Builder builder) {
this.provideRemoteSourceProvider =
RemoteSourceModule_ProvideRemoteSourceFactory.create(builder.remoteSourceModule);
this.dataRepositoryMembersInjector =
DataRepository_MembersInjector.create(
LocalSource_Factory.create(), provideRemoteSourceProvider);
}
@Override
public void inject(DataRepository dataRepository) {
dataRepositoryMembersInjector.injectMembers(dataRepository);
}
public static final class Builder {
private RemoteSourceModule remoteSourceModule;
private Builder() {}
public SourceComponent build() {
if (remoteSourceModule == null) {
this.remoteSourceModule = new RemoteSourceModule();
}
return new DaggerSourceComponent(this);
}
public Builder remoteSourceModule(RemoteSourceModule remoteSourceModule) {
this.remoteSourceModule = Preconditions.checkNotNull(remoteSourceModule);
return this;
}
}
}
複製代碼
當咱們調用靜態的create()
方法後會返回一個DaggerSourceComponent
實例,它是前面聲明的SourceComponent
的實現類。其內部最關鍵的成員變量是DataRepository_MembersInjector
類,它是依賴注入的實際執行者,其內部包含了全部須要注入的實例的Provider
,這些Provider
是在構造時傳入的:
當咱們調用DaggerSourceComponent
的inject
方法時,就會去調用DataRepository_MembersInjector
類的injectMembers
方法,它經過上面這兩個Provider
所提供的get()
方法對目標類中被@Inject
註解的成員變量進行賦值:
對於以上兩種依賴注入方法,其前後順序爲:3.2
> 3.1
,即Component
先會在和它關聯的Module
中尋找,這些關聯的Module
包括:
Module
Component
所關聯的Module
@SubComponent
繼承的Component
關聯的Module
它會在以上三個維度中尋找Module
是否提供了這個類的建立方法(也就是方法的返回值類型爲這個類)
RemoteModule
。@Inject
註解標註的構造方法來建立,例如例子中的LocalSource
。對於每一個目標依賴類,若是在它的建立過程當中依賴於某個參數,那麼就須要先實例化這個參數。這就相似於二叉樹遍歷的過程,在這一遍歷過程當中,若是出現了某個類型不能按照以上兩種方式實例化的時候,那麼會在編譯時拋出異常。