Dagger2 知识梳理(1) Dagger2 依赖注入的两种方式
一、資料推薦
最近這幾天一直在看有關Dagger2有關的文章,感覺就是這東西真難用、真難懂,數次想要放棄,還好有網上大神的教程幫助,模模糊糊總算能把基本的幾個概念跑通了。
這里首先推薦 牛曉偉 的下面一系列文章,其將Dagger2的基本思想講的通俗易懂:
- Android:dagger2 讓你愛不釋手 - 基礎依賴注入框架篇
- Android:dagger2 讓你愛不釋手 - 重點概念講解、融合篇
- Android:dagger2 讓你愛不釋手 - 終結篇
接下來是結合具體例子進行分析的兩篇文章,適合在理解基本思想的前提下對Dagger2有個更直觀的認識:
- AItsuki 的 Dagger2 最清晰的使用教程
- zFxiang_ 的 Dagger2 入門,以初學者角度
還有前同事 Johnny Shieh 的Dagger2系列,里面提到了后來新增的一些用法:
- Dagger 2 完全解析(一),Dagger 2 的基本使用與原理
- Dagger 2 完全解析(二),進階使用 Lazy、Qualifier、Scope 等
- Dagger 2 完全解析(三),Component 的組織關系與 SubComponent
- Dagger 2 完全解析(四),Android 中使用 Dagger 2
- Dagger 2 完全解析(五),Kotlin 中使用 Dagger 2
了解完以上這些基礎文章,就可以嘗試去啃一下官方的英文文檔了,里面介紹了如何在Android中使用Dagger2,但是不得不說,寫的真是晦澀難懂,網上找了很久,也沒有把AndroidInjector說明白的:
- Dagger & Android
- User's Guide
雖然網上的例子很多,但是我們花了那么多時間去看這些文章,本質上還是要用到項目中,這里肯定要優先推薦Google的官方架構Demo:todo-mvp-dagger2,這里面涉及到了很多新的注解用法。
二、導入依賴
在導入依賴的時候,需要考慮當前工程中使用的Gradle插件的版本。如果當前的插件版本小于2.2,那么需要引入 android-apt 插件,Dagger2 入門,以初學者角度 中就是用的這種方式。
在示例代碼中,根目錄下的build.gradle文件配置的Gradle插件版本為2.3.3:
因為大于2.2,所以我只需要在app模塊中的build.gradle文件中,引入以下兩個依賴就好了:三、依賴注入的兩種方式
下面,我們用一個簡單的例子來演示使用Dagger2完成依賴注入的兩種方式:在進行數據的讀寫時,我們可以通過數據管理者DataRepository來管理數據源,外部通過調用這個數據倉庫提供的方法來讀寫數據,完整的源碼可以查看 Dagger2Sample 中第一章。
對于這些數據源的賦值,就可以采用依賴注入的方式來實現。
- DataRepository:目標類。
- DataRepository內部的數據源:目標依賴類。
3.1 在目標依賴類構造函數上使用 @Inject
我們先看第一種方法,通過“在目標依賴類構造函數上使用@Inject,來完成依賴注入”。
第一步
先創建一個LocalSource表示本地的數據源,并在它構造函數上加上@Inject注解。這樣Dagger2在嘗試創建一個LocalSource對象賦值給DataRepository中的mLocalSource變量時,就會調用這個構造函數。
public class LocalSource {public LocalSource() {}public String getData() {return "使用在構造函數上使用 @Inject 的方式,獲取到了本地數據";} } 復制代碼第二步
接下來需要聲明一個用@Component注解的接口或者抽象類,用于注入依賴,這個接口的方法名可以為任意值,但是其形參必須是目標類的具體類型,而返回值只能為void或者目標類的具體類型。
public interface SourceComponent {public void inject(DataRepository dataRepository); } 復制代碼第三步
做好前期這些準備,接下來需要按順序進行以下幾步操作:
- 在目標類中需要被注入的變量上加上@Inject注解。
- make一下工程,讓Dagger2根據SourceComponent中聲明的接口創建一個DaggerSourceComponent實現類。
- 在需要注入的時候調用DaggerSourceComponent.create().inject(this)方法,完成依賴注入。
DataRepository文件如下所示:
public class DataRepository {LocalSource mLocalSource;public DataRepository() {DaggerSourceComponent.create().inject(this);}public String getData() {return mLocalSource.getData();}} 復制代碼流程如下圖所示:
3.2 使用 @Module
3.1的實現方式有一個缺點,就是需要修改構造函數,但是這對于一些第三方的對象來說是不可能做到的,此時就需要通過另一種方法來創建對象。
我們通過下面這個例子來演示:在DataRepository中實例化一個遠程數據源RemoteSource。
第一步
在RemoteSource的構造函數上不再需要添加@Inject注解:
public class RemoteSource {public String getData() {return "使用 @Module 的方式,獲取到了網絡數據";} } 復制代碼第二步
接下來創建一個RemoteSourceModule類,用于提供RemoteSource對象。Dagger2會根據它聲明的方法的返回值類型去識別提供的是哪種類型的對象,這里有兩點需要注意:
- 該Module類需要加上@Module注解
- 提供RemoteSource的方法需要加上@Provides注解
第三步
在SourceComponent中,我們需要告訴它哪些Module可以用來創建目標類所依賴的實例,這里和第一種方式的區別就是需要在@Component后面加上用來創建依賴實例的Module類名:
(modules = {RemoteSourceModule.class}) public interface SourceComponent {public void inject(DataRepository dataRepository); } 復制代碼第四步
而在目標類中,RemoteSource和LocalSource一樣,都需要加上@Inject注解:
public class DataRepository {LocalSource mLocalSource;RemoteSource mRemoteSource;public DataRepository() {DaggerSourceComponent.create().inject(this);}public String getData() {return mLocalSource.getData();}public String getNetData() {return mRemoteSource.getData();} } 復制代碼流程如下圖所示:
3.3 Demo 演示
下面,我們用一個簡單的程序來驗證DataRepository中的mLocalSource/mRemoteSource是否注入成功:
public class RepositoryActivity extends AppCompatActivity {private static final String TAG = RepositoryActivity.class.getSimpleName();private Button mBtnGetData;private Button mBtnGetNetData;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() {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() {public void onClick(View v) {DataRepository repository = new DataRepository();String data = repository.getNetData();Toast.makeText(RepositoryActivity.this, data, Toast.LENGTH_SHORT).show();}});} } 復制代碼運行結果為:
3.4 依賴注入的過程
下面,我們來看一下依賴注入的內部實現,整個依賴注入的入口函數為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();}("unchecked")private void initialize(final Builder builder) {this.provideRemoteSourceProvider =RemoteSourceModule_ProvideRemoteSourceFactory.create(builder.remoteSourceModule);this.dataRepositoryMembersInjector =DataRepository_MembersInjector.create(LocalSource_Factory.create(), provideRemoteSourceProvider);}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.5 依賴查找的過程
對于以上兩種依賴注入方法,其先后順序為:3.2 > 3.1,即Component先會在和它關聯的Module中尋找,這些關聯的Module包括:
- 自己聲明的Module
- 依賴的Component所關聯的Module
- 通過@SubComponent繼承的Component關聯的Module
它會在以上三個維度中尋找Module是否提供了這個類的創建方法(也就是方法的返回值類型為這個類)
- 如果有,那么就通過該方法創建,例如例子中的RemoteModule。
- 如果沒有,那么再通過該類帶有@Inject注解標注的構造方法來創建,例如例子中的LocalSource。
對于每個目標依賴類,如果在它的創建過程中依賴于某個參數,那么就需要先實例化這個參數。這就類似于二叉樹遍歷的過程,在這一遍歷過程中,如果出現了某個類型不能按照以上兩種方式實例化的時候,那么會在編譯時拋出異常。
更多文章,歡迎訪問我的 Android 知識梳理系列:
- Android 知識梳理目錄:www.jianshu.com/p/fd82d1899…
- 個人主頁:lizejun.cn
- 個人知識總結目錄:lizejun.cn/categories/
總結
以上是生活随笔為你收集整理的Dagger2 知识梳理(1) Dagger2 依赖注入的两种方式的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 微软面试+实习总结
- 下一篇: 动画体系知识梳理(1) 转场动画 C