本文爲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的實例了