Dagger2 再學習 dagger2 基本註解類 @Inject 和 @Component 問題 總結:

本文爲dagger2的學習小記,有錯請指正

dagger2 基本註解類

Dagger2 的註解主要有以下七類:

@Inject :

這個註解有兩個作用:在目標類中標記成員變量告訴 Dagger 這個類型
的變量需要一個實例對象;標記依賴類中的構造方法,告訴 Dagger 我可以提供這種類型的依賴實例。

@Component :

用來標記接口或者抽象類,也被稱爲注入器,是 @Inject 和 @Module
的橋樑,所有的 Component 都可以通過它的 modules 知道它所提供的依賴範圍,一個 Componet 可以依賴一個或多個 Component ,並拿到被依賴 Component 暴露出來的實例, Componenet 的 dependencies 屬性就是確定依賴關係的實現。

@Module :

用來標記類,一般類名以 Module 結尾, Module 的主要作用是用來集中管理 @Provides 標記的方法,我們定義一個被 @Module 註解的類, Dagger 就會知道在哪裏找到依賴來滿足創建類的實例, Module 的一個重要特徵是被設計成區塊並可以組合在一起。

@Provides :

對方法進行註解,並且這些方法都是有返回類型的,告訴 Dagger 我
們向如何創建並提供該類型的依賴實例(一般會在方法中 new 出實例),用@Provides 標記的方法,推薦用 provide 作爲前綴。

@Qualifier :

限定符,當一個類的類型不足以標示一個依賴的時候,我們就可以
用這個註解,它會調用 DataModule 中方法來返回合適的依賴類實例。

@Scope :

通過自定義註解來限定作用域,所有的對象都不再需要知道怎麼管理它的
實例, Dagger2 中有一個默認的作用域註解 @Singleton ,通常用來標記在 App 整個生命週期內存活的實例,也可以定義一個@PerActivity 註解,用來表明生命週期要與 Activity 一致。

@SubComponent :

如果我們需要父組件全部的提供對象,我們就可以用包含方式,而不是用依賴方式,包含方式不需要父組件顯示顯露對象,就可以拿到父組件全部
對象,且 SubComponent 只需要在父 Component 接扣中聲明就可以了。

@Inject 和 @Component

第一步:

依賴框架

第二步:

User 作爲目標類中需要實例化的成員對象,給其構造函數添加@Inject標籤:

    public class User {
        public String name;

        @Inject
        public User() {
            name = "lizejun";
        }

        public String getName() {
            return name;
        }
    }

第三步:聲明 Component :

@Component()
public interface OnlyInjectComponent {
    void inject(AnnotationActivity annotationActivity);
}

第四步:在目標類中添加註解 @Inject ,並根據我們第 3 步中聲明的 Component ,調用 DaggerXXX 方法來進行注入:

public class AnnotationActivity extends AppCompatActivity {

    @Inject
    public User mUser;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_dragger2);
        // 在第 3 步聲明的 Component 接口或者抽象類的基礎上,添加 Dagger 前綴。
        DaggerOnlyInjectComponent.builder().build().inject(this);
    }
}

上面這個例子有兩個缺點:

  • 只能標記一個構造方法,因爲如果標記兩個以上,不知道要用哪一個構造提供實例。
  • 不能標記其它我們不能修改的類,例如第三方庫。
  • 如果用 @Inject 標記的構造函數如果有參數,那麼這個參數也需要其它地方提供依賴,而類似於 String 這些我們不能修改的類,只能用 @Module 中的 @Provides來提供實例了。

採用 @Module 來提供依賴

採用 @Module 標記的類提供依賴是常規套路, @Module 標記的類起管理作用,真正提供依賴實例靠的是 @Provides 標記的帶返回類型的方法。

第一步:

和上面類似,我們定義一個依賴類,但是它的構造方法並不需要用@Inject 標記:

public class Person {
    private String name;

    public Person() {
        this.name = "lizejun";
    }

    public String getName() {
        return name;
    }
}

第二步:需要定義一個 @Module 來管理這些依賴類的實例:

@Module
public class PersonDataModule {
    @Provides
    public Person providePerson() {
        return new Person();
    }
}

第三步:定義一個 @Component ,它指向上面定義的 @Module

@Component(modules = { PersonDataModule.class })
public interface PersonInj ectComponent
{
    void inject(PersonInjectActivity injectActivity);
}

第四步:在目標類中進行依賴注入

public class PersonInjectActivity extends Activity {
    @Inject
    Person mPerson;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        DaggerPersonInjectComponent.create().inject(this);
        System.out.println("Person name=" + mPerson.getName());
    }
}

這裏注入的方式有兩種,一種是像上面這樣的,它適合於PersonDataModule 中只有一個無參的構造方法,否則我們需要這樣調用:

DaggerPersonInjectComponent.builder().personDataModule(new PersonDataModule()).build().inject(this);

初始化依賴實例的步驟

  • 查找 Module 中是否存在創建該類型的方法(即 @Component 標記的接口中包含了 @Module 標記的 Module 類,如果沒有則直接查找@Inject 對應的構造方法)。

    • 如果存在創建類方法,則查看該方法是否有參數

      • 如果不存在參數,直接初始化該類的實例,一次依賴注入到此結束。
      • 如果存在參數,則從步驟 1 開始初始化每個參數。
    • 如果不存在創建類方法,則查找該類型的類中有 @Inject 標記的構造方法,查看構造方法是否有參數:

      • 如果不存在參數,則直接初始化該類實例,一次依賴注入到此結束。
      • 如果存在參數,則從步驟 1 開始初始化每個參數。

@Qualifier

在 Dagger 中,有一個已經定義好的限定符, @Name ,下面我們也自己定義一個限定符:

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface PeopleThreeQualifier {
}

第一步:和前面類似,我們先定義一個需要實例化的依賴類:

public class People {
    private int count;

    public People() {
        count = 0;
    }

    public People(int count) {
    this.count = count;

    public int getCount() {
        return count;
    }
}

第二步:定義一個 DataModule

和前面不同的是,在它的provideXXX 方法的註解中,我們添加了@Name(xxx) 和自定義的註解 PeopleThreePeople :

@Modulepublic
class PeopleDataModule {
    @Provides
    @Named("Five People")
    People provideFivePeople() {
        return new People(5);
    }

    @Provides
    @Named("Ten People")
    People provideTenPeople() {
        return new People(10);
    }

    @Provides
    @PeopleThreeQualifier
    People provideThreePeople() {
        return new People(3);
    }
}

第三步:定義 Component

@Component(modules = PeopleDataModule.class)
public interface PeopleInjec tComponent
{
    void inject(PeopleInjectActivity peopleInjectActivity);
}

第四步:定義依賴的module

在目標類中進行依賴注入,在提供 @Inject 註解時,我們還需要聲明和PeopleDataModule 中對應的限定符,這樣 Dagger 就知道該用那個函數來生成目標類中的依賴類實例:

public class PeopleInjectActivity extends Activity {
    @Inject
    @Named("Five People")
    People mFivePeople;

    @Inject
    @Named("Ten People")
    People mTenPeople;

    @Inject
    @PeopleThreeQualifier
    People mThreePeople;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        DaggerPeopleInjectComponent.builder().peopleDataModule(new PeopleDataModule()).build().inject(this);
    }
}

@Scope

@Scope 的作用主要是在組織 Component 和 Module 的時候起到一個提醒和管理的作用,在 Dagger 中,有一個默認的作用域@Singleton 。@Scope 的作用是: Dagger2 可以通過自定義Scope 註解,來限定通過 Module 和Inject 方式創建的類的實例的生命週期能夠與目標類的生命週期相同。 Scope 的真正作用在與 Component 的組織:

  • 更好的管理 Component 之間的組織方式,不管是依賴方式還是包含方式,都有必要用自定的 Scope 註解標註這些 Component ,而且編譯器會檢查有依賴關係或包含關係的 Component ,若發現有Component 沒有用自定義 Scope 註解,則會報錯。

  • 更好地管理 Component 與 Module 之間地關係,編譯器會檢查 Component 管理的Module ,若發現 Component 的自定義 Scope 註解與 Module 中的標註創建類實例方法的註解不一樣,就會報錯。

  • 提高程序的可讀性。

下面是一個使用 @Singleton 的例子:

第一步:定義需要實例化的類:

public class AnSingleObject {
    private String objectId;

    public AnSingleObject() {
        objectId = toString();
    }

    public String getObjectId() {
        return objectId;
    }
}

第二步:定義 DataModule

在它的 provideXXX 方法,提供了 @Singletion 註解:

@Module
public class AnSingleObjectDataModule {
    @Provides
    @Singleton
    AnSingleObject provideAnSingleObject() {
        return new AnSingleObject();
    }
}

第三步:定義 Component

和前面不同的是,需要給這個 Component 添加 @Singleton註解:

@Component(modules = { AnSingleObjectDataModule.class })
@Singleton
public abstract class AnSingleObjectInjectComponent {
    private static AnSingleObjectInjectComponent sInstance;

    public abstract void inject(AnSingleObjectInjectActivity anSingleObjectInjectActivity);

    public static AnSingleObjectInjectComponent getInstance() {
        if (sInstance == null) {
            sInstance = DaggerAnSingleObjectInjectComponent.create();
        }
        return sInstance;
    }
}

第四步:在目標類中進行依賴注入

每次啓動 Activity 的時候,我們可以發現打印出來的 hash 值都是相同的:

public class AnSingleObjectInjectActivity extends Activity {
    @Inject
    AnSingleObject object;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        AnSingleObjectInjectComponent.getInstance().inject(this);
        System.out.println("AnSingleObject id=" + object.getObjectId());
    }
}

Component

Component 有三種組織方式:

  • 依賴:一個 Component 依賴一個或多個 Component ,採用的是@Component 的dependencies 屬性。
  • 包含:這裏就用到了 @SubComponent 註解,用它來標記接口或者抽象類,表示它可以被包乾。一個 Component 可以包含一個或多個Component ,而且被包含的Component 還可以繼續包含其它的Component 。
  • 繼承:用一個 Component 繼承另外一個 Component 。

問題

Unused Modules and Component Dependencies
https://dagger.dev/unused-modules

當Dagger處理器生成組件時,它只需要模塊和組件依賴項的實例,這些實例是爲綁定提供請求所顯式需要的。

如果組件中使用的所有模塊方法都是靜態的,Dagger根本不需要該模塊的實例。Dagger可以在不使用模塊的情況下直接調用靜態方法。
如果模塊沒有爲組件提供綁定,則不需要該模塊的實例來構造圖。

模塊:

provider方法不是靜態的

@Module
public class ModuleA{
    @Provides
    @IntoSet
    String provideOneString(LocationManager depA, Context depB) {
        return "ABC";
    }
}

組件:MainActivityComponent

@Component(dependencies = ApplicationComponent.class,modules ={ModuleB.class,ModuleA.class})
public interface MainActivityComponent {
    void inject(MainActivity mainActivity);
}
dagger2編譯的:DaggerMainActivityComponent - Builder()

分析:moduleB的provider方法都是靜態的,所以dagger不需要該模塊的實例,所以,DaggerMainActivityComponent build的時候不需要moduleB實例用於調用ModuleB_ProvideSomeStringsFactory(moduleB方法的工廠類)去提供對象

    @Deprecated
    public Builder moduleB(ModuleB moduleB) {
      Preconditions.checkNotNull(moduleB);
      return this;
    }

分析:moduleA就不一樣,moduleA中有非靜態的方法,所以,需要DaggerMainActivityComponent初始化時候傳入moduleA實例,因爲DaggerMainActivityComponent 讓 moduleA的非靜態方法的工廠類提供對象時候用到該moduleA實例

    public Builder moduleA(ModuleA moduleA) {
      this.moduleA = Preconditions.checkNotNull(moduleA);
      return this;
    }
    
    public Builder applicationComponent(ApplicationComponent applicationComponent) {
      this.applicationComponent = Preconditions.checkNotNull(applicationComponent);
      return this;
    }

總結:

依賴方,組件,模塊,工廠類:

1:組件連接組裝component給依賴方:
component.builder - 傳入module實例 - build:initialize :module工廠類用module製作provider:factory對象{moudle, module獲取application}

2:inject 注入給依賴方
DaggerApplicationComponent:inject - Application_MembersInjector(application, 各個provider.get factory生產的對象(其實就是去module裏取))

3:如果module中的靜態方法,component-module:去獲取對象時候是不需要module實例的,所以,component在依賴方中初始化就不用傳module實例,所以,上面的@Deprecated public Builder moduleB,因爲moduleB是靜態方法,用不到module實例,所以,初始化component時候也不用設置moduleB的實例了

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章