Dagger2 知識梳理(1) Dagger2 依賴注入的兩種方式

1、資料推薦

最近這幾天一直在看有關Dagger2有關的文章,感受就是這東西真難用、真難懂,數次想要放棄,還好有網上大神的教程幫助,模模糊糊總算能把基本的幾個概念跑通了。html

這裏首先推薦 牛曉偉 的下面一系列文章,其將Dagger2的基本思想講的通俗易懂:java

接下來是結合具體例子進行分析的兩篇文章,適合在理解基本思想的前提下對Dagger2有個更直觀的認識:android

還有前同事 Johnny ShiehDagger2系列,裏面提到了後來新增的一些用法:git

瞭解完以上這些基礎文章,就能夠嘗試去啃一下官方的英文文檔了,裏面介紹瞭如何在Android中使用Dagger2,可是不得不說,寫的真是晦澀難懂,網上找了好久,也沒有把AndroidInjector說明白的:github

雖然網上的例子不少,可是咱們花了那麼多時間去看這些文章,本質上仍是要用到項目中,這裏確定要優先推薦Google的官方架構Demotodo-mvp-dagger2,這裏面涉及到了不少新的註解用法。網絡

2、導入依賴

在導入依賴的時候,須要考慮當前工程中使用的Gradle插件的版本。若是當前的插件版本小於2.2,那麼須要引入 android-apt 插件,Dagger2 入門,以初學者角度 中就是用的這種方式。架構

在示例代碼中,根目錄下的build.gradle文件配置的Gradle插件版本爲2.3.3app

由於大於 2.2,因此我只須要在 app模塊中的 build.gradle文件中,引入如下兩個依賴就行了:

3、依賴注入的兩種方式

下面,咱們用一個簡單的例子來演示使用Dagger2完成依賴注入的兩種方式:在進行數據的讀寫時,咱們能夠經過數據管理者DataRepository來管理數據源,外部經過調用這個數據倉庫提供的方法來讀寫數據,完整的源碼能夠查看 Dagger2Sample 中第一章。框架

對於這些數據源的賦值,就能夠採用依賴注入的方式來實現。ide

  • DataRepository:目標類。
  • DataRepository內部的數據源:目標依賴類。

3.1 在目標依賴類構造函數上使用 @Inject

咱們先看第一種方法,經過「在目標依賴類構造函數上使用@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.2 使用 @Module

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);
}
複製代碼

第四步

而在目標類中,RemoteSourceLocalSource同樣,都須要加上@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();
    }
}
複製代碼

流程以下圖所示:

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;

    @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();
            }
        });
    }
}
複製代碼

運行結果爲:

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();
  }

  @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是在構造時傳入的:

當咱們調用DaggerSourceComponentinject方法時,就會去調用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 知識梳理系列:

相關文章
相關標籤/搜索