Dagger 2 系列(六) -- 進階篇:Component 依賴、@SubComponent 與多 Component 下的 Scope 使用限制

leeGYPlus發表於2018-09-25

Dagger2

  • 該系列部落格的最終目標: 搭建 MVP + Dagger2 框架
  • 該系列部落格包含以下幾篇內容:
  1. Dagger 2 系列(一) -- 前奏篇:依賴注入的基本介紹
  2. Dagger 2 系列(二) -- 基礎篇:@Inject、@Component
  3. Dagger 2 系列(三) -- 基礎篇:@Module 和@Provides
  4. Dagger 2 系列(四) -- 基礎篇:@Named 和 @Qualifier
  5. Dagger 2 系列(五) -- 進階篇:@Scope 和 @Singleton
  6. Dagger 2 系列(六) -- 進階篇:Component 依賴、@SubComponent 與多 Component 下的 Scope 使用限制

在這篇文章中你會看到什麼:

  1. Module 依賴 是什麼
  2. @SubComponent 是什麼
  3. Module 依賴和 @SubComponent 不同
  4. 多 Component 下的 Scope 的使用限制

通過該系列的前幾篇部落格的學習,應該基本掌握了 Dagger2 的基本概念、基本使用方法、Scope 的概念等,這些足夠我們搭建一個簡單 Demo 去了解 Dagger2 ,但是在正常的業務開發中僅僅瞭解這些就顯得力不從心,在這篇中我們就來了解一下 Component 依賴@SubComponent 的基本使用以及多 Compone 下的 Scope 的使用。

Component 依賴@SubComponent 均是在多 Component 下通過兩者來組織 Component 的依賴關係,具體的依賴關係的建立是根據業務依賴建立。

1. Component 依賴

Component 依賴 是通過 @Component 的註解中 dependencies 選項來標識的,意思是指 該 Component 依賴 dependencies 指定的 Component

舉個例子,看以下程式碼:

  • SubComponent
@Component(dependencies = AllComponent.class, modules = SubModule.class)
public interface SubComponent {
    void injectSubComponentActivity(ComponentDepActivity mDepActivity);
}
複製程式碼

通過 dependencies = AllComponent.class 可知,SubComponent 依賴 AllComponent

  • AllComponent
@Component(modules = AllModule.class)
public interface AllComponent {
    AllBean getAllBean();
}
複製程式碼

如果要想讓依賴 AllComponentSubComponent 提供注入 AllBean 例項物件,那麼需要在 AllComponent 顯式 的提供獲得 AllBean 的方法,在本例中為 AllBean getAllBean(),否則將報出類似以下的錯誤:

錯誤: xxxx.AllBean cannot be provided without an @Inject constructor or from an @Provides- or @Produces-annotated method.
xxxx.ComponentDepActivity.mAllBean
[injected field of type: xxxx.AllBean mAllBean]
複製程式碼

意思大致為 ComponentDepActivity 中不能正確的注入 AllBean 。

  • ComponentDepActivity
public class ComponentDepActivity extends AppCompatActivity {

    @Inject
    SubBean mSubBean;
    @Inject
    AllBean mAllBean;
    AllComponent mAllComponent;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_component_dep);
        mAllComponent =  DaggerAllComponent.builder().allModule(new AllModule()).build();
        DaggerSubComponent.builder().allComponent(mAllComponent).build().injectSubComponentActivity(this);
        mSubBean.test();
        mAllBean.test();
    }
}
複製程式碼
  • 列印日誌
E/TAG: test :  SubBean
E/TAG: test :  AllBean
複製程式碼

具體程式碼見: Dagger2Demo

2. @SubComponent

@SubComponent 也是管理 Component 間的依賴,不同的是這種方式不需要 在被依賴的 Component 中顯式的宣告可以獲取相應類例項的方法。通過 @SubComponent 來管理的 Component 之間是一種 繼承關係子 Component 理所當然的可以使用父 Component 的可以提供的類例項

具體使用如下:

  • SubComponent
@Subcomponent(modules = SubModule.class)
public interface SubComponent {
    void injectSubComponentActivity(ComponentSubActivity mDepActivity);
}
複製程式碼
  • AllComponent
@Component(modules = AllModule.class)
public interface AllComponent {
    SubComponent addSub(SubModule mSubModule);
}
複製程式碼

要在父 Component 中顯式的宣告 子 Component ,其具體格式為 :

子Component 方法名 (子Component 對應的 Module);
複製程式碼
  • ComponentSubActivity
public class ComponentSubActivity extends AppCompatActivity {

    @Inject
    AllBean mAllBean;

    @Inject
    SubBean mSubBean;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_component_sub);
        AllComponent mAllComponent = DaggerAllComponent.builder().build();
        mAllComponent.addSub(new SubModule()).injectSubComponentActivity(this);
        mAllBean.test();
        mSubBean.test();
    }
}
複製程式碼
  • 執行結果
test :  AllBean
test :  SubBean
複製程式碼

具體程式碼見:Dagger2Demo

3. Component 依賴@SubComponent 的不同

關於兩者的不同 Dependency injection with Dagger 2 - Custom scopes 有以下解釋:

In general we have two ways to do this - with @Subcomponent annotation or with Components dependencies. The main difference between them is an objects graph sharing. Subcomponents have access to entire objects graph from their parents while Component dependency gives access only to those which are exposed in Component interface.

大致意思如下:

Subcomponent 可以訪問到 父 Component 的全部物件圖,而 Component 只可以訪問到在所依賴的 Component 中暴露出來的類。

我們看另外一段解釋:

And what is more important all scoping stuff happens here. All instances taken from UserComponent inherited from AppComponent still are singletons (in Application scope). But those which are produced by UserModule (which is a part of UserComponent) will be “local singletons” which live as long as this UserComponent instance.

大致意思如下:

更重要的是,所有範圍內的事情都發生在這裡。從AppComponent繼承的UserComponent獲取的所有例項仍然是單例(在應用程式範圍內)。但是由UserModule(它是UserComponent的一部分)生成的那些將是“本地單例”,其存在與此UserComponent例項一樣長。

我們也可以這樣概括:子元件可以訪問到父元件中提供的例項,並且該例項的scope 和父元件定義的scope 相同

4. 多 Component 與 Scope 的使用

兩者同時使用有以下限制:

  1. Component 和他所依賴的 Component 不能公用相同的 Scope,每個Component 都要有自己的 Scope,編譯時會報錯,因為這有可能破壞Scope的範圍。
  2. @Singleton 的 Component 不能依賴其他 Component。正常來說使用 @Singleton 註解的Component應為全域性的Component 。
  3. 無Scope的Component不能依賴有Scope的Component,因為這也會導致Scope 被破壞。
  4. Module 或通過建構函式注入依賴圖的類和其 Component 必須使用相同的Scope 。

5. 總結

在日常的專案開發過程中,我們需要認真的考慮 Component 間的依賴關係,一般的,這些依賴關係的建立是根據具體的業務,就比如在 GitHubClient 專案中,UserComponent 依賴 AppComponentRepositoriesListActivityComponentRepositoryDetailsActivityComponent 依賴 UserComponent ,這就是按照業務線: 庫列表庫詳情 是依賴於 使用者 的,而 使用者 是被 App 所管理。

在多 Component 下 Scope 的使用是有限制的, 其最終的標準都是不可以導致 Scope 被破壞。


參考資料

Dependency injection with Dagger 2 - the API

Dependency injection with Dagger 2 - Custom scopes

Dependency injection with Dagger 2 - Custom scopes

Dagger2:Scope、Component 間依賴和 @SubComponent

相關文章