一、概述
Dagger2
依賴注入框架的好處:
- 依賴的注入和配置獨立于組件之外
- 依賴對象是在一個獨立装哆、不耦合的地方初始化,當初始化方式改變的時候修改的代碼少。
- 依賴注入使得單元測試更加簡單瞎嬉。
Dagger2
相對于其它框架的優(yōu)點:
- 編譯期生成代碼,有錯誤會在編譯期報出厚柳。
- 錯誤可追蹤氧枣。
- 易于調(diào)試。
Dagger2
的缺點:
- 缺少靈活性别垮。
- 沒有動態(tài)機制挑胸。
二、Dagger2
的注解
Dagger2
的注解主要有以下七類:
-
@Inject
:這個注解有兩個作用:在目標類中標記成員變量告訴Dagger
這個類型的變量需要一個實例對象宰闰;標記依賴類中的構(gòu)造方法茬贵,告訴Dagger
我可以提供這種類型的依賴實例。 -
@Component
:用來標記接口或者抽象類移袍,也被稱為注入器解藻,是@Inject
和@Module
的橋梁,所有的Component
都可以通過它的modules
知道它所提供的依賴范圍葡盗,一個Componet
可以依賴一個或多個Component
螟左,并拿到被依賴Component
暴露出來的實例啡浊,Componenet
的dependencies
屬性就是確定依賴關(guān)系的實現(xiàn)。 -
@Module
:用來標記類胶背,一般類名以Module
結(jié)尾巷嚣,Module
的主要作用是用來集中管理@Provides
標記的方法,我們定義一個被@Module
注解的類钳吟,Dagger
就會知道在哪里找到依賴來滿足創(chuàng)建類的實例廷粒,Module
的一個重要特征是被設(shè)計成區(qū)塊并可以組合在一起。 -
@Provides
:對方法進行注解红且,并且這些方法都是有返回類型的坝茎,告訴Dagger
我們向如何創(chuàng)建并提供該類型的依賴實例(一般會在方法中new
出實例),用@Provides
標記的方法暇番,推薦用provide
作為前綴嗤放。 -
@Qualifier
:限定符,當一個類的類型不足以標示一個依賴的時候壁酬,我們就可以用這個注解次酌,它會調(diào)用DataModule
中方法來返回合適的依賴類實例。 -
@Scope
:通過自定義注解來限定作用域舆乔,所有的對象都不再需要知道怎么管理它的實例和措,Dagger2
中有一個默認的作用域注解@Singleton
,通常用來標記在App
整個生命周期內(nèi)存活的實例蜕煌,也可以定義一個@PerActivity
注解派阱,用來表明生命周期要與Activity
一致。 -
@SubComponent
:如果我們需要父組件全部的提供對象斜纪,我們就可以用包含方式贫母,而不是用依賴方式,包含方式不需要父組件顯示顯露對象盒刚,就可以拿到父組件全部對象腺劣,且SubComponent
只需要在父Component
接扣中聲明就可以了。
三因块、Dagger2
的簡單應(yīng)用 - @Inject
和@Component
第一步:基礎(chǔ)配置橘原,在build.gradle
中添加相應(yīng)的依賴:
//添加(1)
apply plugin: 'com.neenbedankt.android-apt'
buildscript {
repositories {
jcenter()
}
dependencies {
//添加(2)
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
}
}
android {
compileSdkVersion 23
buildToolsVersion "23.0.0"
defaultConfig {
applicationId "com.demo.zejun.repodragger2"
minSdkVersion 15
targetSdkVersion 23
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.0.0'
//添加(3)
apt 'com.google.dagger:dagger-compiler:2.0'
//添加(4)
compile 'com.google.dagger:dagger:2.0'
}
第二步:User
作為目標類中需要實例化的成員對象,給其構(gòu)造函數(shù)添加@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
涡上,并根據(jù)我們第3步中聲明的Component
趾断,調(diào)用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接口或者抽象類的基礎(chǔ)上,添加Dagger前綴吩愧。
DaggerOnlyInjectComponent.builder().build().inject(this);
}
}
上面這個例子有兩個缺點:
- 只能標記一個構(gòu)造方法芋酌,因為如果標記兩個以上,不知道要用哪一個構(gòu)造提供實例雁佳。
- 不能標記其它我們不能修改的類脐帝,例如第三方庫同云。
- 如果用
@Inject
標記的構(gòu)造函數(shù)如果有參數(shù),那么這個參數(shù)也需要其它地方提供依賴堵腹,而類似于String
這些我們不能修改的類炸站,只能用@Module
中的@Provides
來提供實例了。
四疚顷、采用@Module
來提供依賴
采用@Module
標記的類提供依賴是常規(guī)套路旱易,@Module
標記的類起管理作用,真正提供依賴實例靠的是@Provides
標記的帶返回類型的方法荡含。
第一步:和上面類似,我們定義一個依賴類届垫,但是它的構(gòu)造方法并不需要用@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 PersonInjectComponent {
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
中只有一個無參的構(gòu)造方法误债,否則我們需要這樣調(diào)用:
DaggerPersonInjectComponent.builder().personDataModule(new PersonDataModule()).build().inject(this);
五、初始化依賴實例的步驟
查找
Module
中是否存在創(chuàng)建該類型的方法(即@Component
標記的接口中包含了@Module
標記的Module
類妄迁,如果沒有則直接查找@Inject
對應(yīng)的構(gòu)造方法)寝蹈。如果存在創(chuàng)建類方法,則查看該方法是否有參數(shù)
如果不存在參數(shù)登淘,直接初始化該類的實例箫老,一次依賴注入到此結(jié)束。
如果存在參數(shù)黔州,則從步驟1開始初始化每個參數(shù)耍鬓。
如果不存在創(chuàng)建類方法,則查找該類型的類中有
@Inject
標記的構(gòu)造方法流妻,查看構(gòu)造方法是否有參數(shù):如果不存在參數(shù)牲蜀,則直接初始化該類實例,一次依賴注入到此結(jié)束绅这。
如果存在參數(shù)涣达,則從步驟1開始初始化每個參數(shù)。
六证薇、@Qualifier
限定符
在Dagger
中度苔,有一個已經(jīng)定義好的限定符,@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
:
@Module
public 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 PeopleInjectComponent {
void inject(PeopleInjectActivity peopleInjectActivity);
}
第四步:在目標類中進行依賴注入完残,在提供@Inject
注解時,我們還需要聲明和PeopleDataModule
中對應(yīng)的限定符横漏,這樣Dagger
就知道該用那個函數(shù)來生成目標類中的依賴類實例:
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);
System.out.println("Five People=" + mFivePeople.getCount() + ",Ten People=" + mTenPeople.getCount() + ", Three People=" + mThreePeople.getCount());
}
}
七谨设、@Scope
@Scope
的作用主要是在組織Component
和Module
的時候起到一個提醒和管理的作用,在Dagger
中缎浇,有一個默認的作用域@Singleton
扎拣。
@Scope
的作用是:Dagger2
可以通過自定義Scope
注解,來限定通過Module
和Inject
方式創(chuàng)建的類的實例的生命周期能夠與目標類的生命周期相同素跺。Scope
的真正作用在與Component
的組織:
- 更好的管理
Component
之間的組織方式二蓝,不管是依賴方式還是包含方式,都有必要用自定的Scope
注解標注這些Component
指厌,而且編譯器會檢查有依賴關(guān)系或包含關(guān)系的Component
刊愚,若發(fā)現(xiàn)有Component
沒有用自定義Scope
注解,則會報錯踩验。 - 更好地管理
Component
與Module
之間地關(guān)系鸥诽,編譯器會檢查Component
管理的Module
,若發(fā)現(xiàn)Component
的自定義Scope
注解與Module
中的標注創(chuàng)建類實例方法的注解不一樣箕憾,就會報錯牡借。 - 提高程序的可讀性。
下面是一個使用@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
的時候,我們可以發(fā)現(xiàn)打印出來的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
還可以繼續(xù)包含其它的Component
犀填。 - 繼承:用一個
Component
繼承另外一個Component
。
九嗓违、Google
官方框架分析
下面是Google
官方框架的目錄結(jié)構(gòu):
可以看出九巡,它把每個界面都作為一個獨立的包(
addedittask、statistics蹂季、taskdetail冕广、tasks
)疏日,而數(shù)據(jù)、依賴類是其它的兩個包(data撒汉、util
)沟优,我們先從ToDoApplication
開始分析:
public class ToDoApplication extends Application {
private TasksRepositoryComponent mRepositoryComponent;
@Override
public void onCreate() {
super.onCreate();
mRepositoryComponent = DaggerTasksRepositoryComponent.builder()
.applicationModule(new ApplicationModule((getApplicationContext())))
.build();
}
public TasksRepositoryComponent getTasksRepositoryComponent() {
return mRepositoryComponent;
}
}
在ToDoApplication
中,我們實例化了一個變量TasksRepositoryComponent
睬辐,它相當于是項目中所有其它Component
的管理者挠阁,它被聲明為@Singleton
的,即在App
的生命周期中只存在一個溯饵,同時用@Component
表明它和TaskRepositoyModule
侵俗、ApplicationModule
這兩個Module
關(guān)聯(lián)。
@Singleton
@Component(modules = {TasksRepositoryModule.class, ApplicationModule.class})
public interface TasksRepositoryComponent {
TasksRepository getTasksRepository();
}
TaskRpositotyModule
提供了兩種類型的數(shù)據(jù)源對象丰刊,它們是用@Local
隘谣、@Remote
來區(qū)分的:
@Module
public class TasksRepositoryModule {
@Singleton
@Provides
@Local
TasksDataSource provideTasksLocalDataSource(Context context) {
return new TasksLocalDataSource(context);
}
@Singleton
@Provides
@Remote
TasksDataSource provideTasksRemoteDataSource() {
return new FakeTasksRemoteDataSource();
}
}
接下來再看一下ApplicationModule
@Module
public final class ApplicationModule {
private final Context mContext;
ApplicationModule(Context context) {
mContext = context;
}
@Provides
Context provideContext() {
return mContext;
}
}
下面我們用一個比較簡單的界面來看一下TasksRepositoryComponent
是怎么和其它的Component
關(guān)聯(lián)起來的,首先看StatisticsActivity
:
public class StatisticsActivity extends AppCompatActivity {
private DrawerLayout mDrawerLayout;
@Inject
StatisticsPresenter mStatiticsPresenter; //依靠Dagger實例化的對象藻三。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.statistics_act);
//初始化Fragment界面洪橘。
StatisticsFragment statisticsFragment = (StatisticsFragment) getSupportFragmentManager().findFragmentById(R.id.contentFrame);
if (statisticsFragment == null) {
statisticsFragment = StatisticsFragment.newInstance();
ActivityUtils.addFragmentToActivity(getSupportFragmentManager(),
statisticsFragment, R.id.contentFrame);
}
DaggerStatisticsComponent.builder()
.statisticsPresenterModule(new StatisticsPresenterModule(statisticsFragment))
.tasksRepositoryComponent(((ToDoApplication) getApplication())
.getTasksRepositoryComponent())
.build().inject(this);
}
}