Dagger2
依賴注入框架的好處:android
Dagger2
相對於其它框架的優勢:bash
Dagger2
的缺點:app
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
接扣中聲明就能夠了。Dagger2
的簡單應用 - @Inject
和@Component
第一步:基礎配置,在build.gradle
中添加相應的依賴:ide
//添加(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
做爲目標類中須要實例化的成員對象,給其構造函數添加@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);
}
}
複製代碼
上面這個例子有兩個缺點:gradle
@Inject
標記的構造函數若是有參數,那麼這個參數也須要其它地方提供依賴,而相似於String
這些咱們不能修改的類,只能用@Module
中的@Provides
來提供實例了。@Module
來提供依賴採用@Module
標記的類提供依賴是常規套路,@Module
標記的類起管理做用,真正提供依賴實例靠的是@Provides
標記的帶返回類型的方法。 第一步:和上面相似,咱們定義一個依賴類,可是它的構造方法並不須要用@Inject
標記:ui
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
中只有一個無參的構造方法,不然咱們須要這樣調用:
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
:
@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
中對應的限定符,這樣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);
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
方式建立的類的實例的生命週期可以與目標類的生命週期相同。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
。Google
官方框架分析下面是Google
官方框架的目錄結構:
addedittask、statistics、taskdetail、tasks
),而數據、依賴類是其它的兩個包(
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
關聯。
@Singleton
@Component(modules = {TasksRepositoryModule.class, ApplicationModule.class})
public interface TasksRepositoryComponent {
TasksRepository getTasksRepository();
}
複製代碼
TaskRpositotyModule
提供了兩種類型的數據源對象,它們是用@Local
、@Remote
來區分的:
@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
關聯起來的,首先看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);
}
}
複製代碼