Dagger2
做爲一個上手難度較高的框架,我也是看了許多相關的文章,經歷了無數次的從入門到放棄。放棄的多了好像也有一點懂了,因而乎我也總結一下本身對Dagger2
使用的相關知識的理解。git
關於Dagger2
首先要理解的就是依賴注入(DI)和控制反轉(IOC),對這兩個概念你若是已經有所瞭解,能夠直接跳到下一節。github
在理解依賴注入以前先了解依賴注入的目的,也是使用Dagger2
框架的目的,知道了目的才能更好地理解過程。依賴注入的目的就是二個字:解耦。編程
高內聚,低耦合,是面向對象編程提倡遵循的設計原則,而經過依賴注入的方式能實現控制反轉,從而實現解耦的目的。bash
光這樣說仍是太理論了,不易理解,舉個例子來幫助理解下,最近復聯4大火,舉個漫威英雄的例子。網絡
首先想象一下,你是個自帶柯南屬性普通人,每次有外星人入侵或者是超能力變種人搞破壞,你都好巧不巧的能出如今現場,然而你並無能力戰勝他們,你只有託尼史塔克的聯繫方式,因此你每次都聯繫他,由他來解決這些麻煩。然而託尼忽然有一天告訴你他要帶着小辣椒去度假,會有一段時間聯繫不上。你沒辦法出於求生本能,你得去尋找另外一個超級英雄,你找到了美國隊長,隊長一口答應,說沒問題,下次有事聯繫我,我來搞定。因而你得到了美國隊長的聯繫方式。又過一段時間,隊長也和你說他要和冬兵去度假,然而託尼還度假沒回來。你沒辦法,又只能本身去找寡姐,得到了他的聯繫方式,下次遇到襲擊就聯繫她。發現了沒有,這裏你每次都依賴與某一個超級英雄,一旦發生變故,你只能本身去找新的英雄得到更新他的聯繫方式。這樣對某個英雄依賴很是嚴重,如今換一種方法,不具體依賴某個英雄,我直接去神盾局找尼克弗瑞獲得他的聯繫方式,之後有事都聯繫他,至因而哪一個超級英雄來解決又或者怎麼去聯繫英雄,我都不用知道,交給尼克弗瑞去處理就行。架構
下面經過代碼加深理解,先定義三個具體英雄類:app
//抽象英雄類
public abstract class Hero {
public abstract String call();
}
複製代碼
public class IronMan extends Hero {
@Override
public String call() {
return "賈維斯:已收到您的求救消息,正在聯繫託尼";
}
}
複製代碼
public class CaptainAmerica extends Hero {
@Override
public String call() {
return "美國隊長:我已收到您的求救消息,正在趕來的路上";
}
}
複製代碼
public class BlackWidow extends Hero {
@Override
public String call() {
return "黑寡婦:我已收到您的求救消息,正在趕來的路上";
}
}
複製代碼
最後是本身類:框架
public class Self {
private IronMan ironMan;
public Self() {
ironMan = new IronMan();
}
public void help() {
String call = ironMan.call();
Log.d("callMessage", call);
}
}
複製代碼
調用:ide
//首先要有一個我
Self self = new Self();
//遇到危險
self.help();
複製代碼
執行日誌:函數
D/callMessage: 賈維斯:已收到您的求救消息,正在聯繫託尼
複製代碼
託尼去度假了,因而咱們只能聯繫美隊,因此要修改Self
類:
public class Self {
// private IronMan ironMan;
private CaptainAmerica captainAmerica;
public Self() {
// ironMan = new IronMan();
captainAmerica = new CaptainAmerica();
}
public void help() {
// String call = ironMan.call();
String call = captainAmerica.call();
Log.d("callMessage", call);
}
}
複製代碼
執行日誌:
D/callMessage: 美國隊長:我已收到您的求救消息,正在趕來的路上
複製代碼
美隊也去度假了,再次修改Self
類:
public class Self {
// private IronMan ironMan;
// private CaptainAmerica captainAmerica;
private BlackWidow blackWidowa;
public Self() {
// ironMan = new IronMan();
// captainAmerica = new CaptainAmerica();
blackWidowa = new BlackWidow();
}
public void help() {
// String call = ironMan.call();
// String call = captainAmerica.call();
String call = blackWidowa.call();
Log.d("callMessage", call);
}
}
複製代碼
執行日誌:
D/callMessage: 黑寡婦:我已收到您的求救消息,正在趕來的路上
複製代碼
看到這裏發現每次變更都要修改Self
類,這裏Self
一直依賴一個英雄類,英雄更換了要修改,英雄的構造函數變了也要修改Self
類,這樣耦合就很是嚴重。如今咱們採用經過尼克弗瑞來聯繫英雄:
public class NickFury {
private Hero hero;
public Hero call() {
hero = new CaptainAmerica();
return hero;
}
}
複製代碼
修改Self
類:
public class Self {
private NickFury nickFury;
public Self() {
nickFury = new NickFury();
}
public void help() {
Hero hero = nickFury.call();
String call = hero.call();
Log.d("callMessage", call);
}
}
複製代碼
執行日誌:
D/callMessage:美國隊長:我已收到您的求救消息,正在趕來的路上
複製代碼
這下Self
類不依賴於具體某個英雄類,而是經過三方NickFury
類來實現英雄對象的注入。一旦有所變更更換英雄,只須要修改NickFury
類的方法便可。其實這裏的NickFury類,相似於工廠模式。
還記的依賴注入的目的嗎?解耦,這裏經過第三方工廠類使具體英雄類與Self
類再也不耦合,原來是Self
主動去new
實例化一個英雄類,修改後變爲被動經過調用第三方類方法注入一個英雄類,由主動到被動實現了控制反轉,實現瞭解耦,達成了這個目的。
關於依賴注入的方法有如下幾種:
基於接口:
public interface InjectInterface {
void injectHero(Hero hero);
}
複製代碼
public class MySelf implements InjectInterface {
Hero hero;
public void help() {
String call = hero.call();
Log.d("callMessage", call);
}
@Override
public void injectHero(Hero hero) {
this.hero = hero;
}
}
複製代碼
定義一個接口,實現類實現接口方法注入。
基於構造函數:
public class MySelf {
private Hero hero;
public MySelf(Hero hero) {
this.hero = hero;
}
public void help() {
String call = hero.call();
Log.d("callMessage", call);
}
}
複製代碼
在構造函數時傳入。
基於set方法:
public class MySelf {
private Hero hero;
public void setHero(Hero hero) {
this.hero = hero;
}
public void help() {
String call = hero.call();
Log.d("callMessage", call);
}
}
複製代碼
經過set方法注入對象。
基於註解注入:
Dagger2
中就用到了註解。因此這裏用Dagger2
來實現一個了依賴注入的例子。再想象一個吃麻辣燙場景,麻辣燙裏要加不少食材,好比牛肉、豆腐、香腸、魚丸等而香腸又是由腸衣和肉餡組成,魚丸是由魚肉作成。因此代碼通常是這樣寫:
public class TestActivity extends AppCompatActivity {
Fish fish;
FishBall fishBall;
Doufu doufu;
Potato potato;
Meat meat;
Casings casings;
SeeYouTomrrow seeYouTomrrow;
Sausage sausage;
SpicyHotPot spicyHotPot;
Beef beef;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
beef = new Beef();
fish = new Fish();
fishBall = new FishBall(fish);
doufu = new Doufu();
potato = new Potato();
meat = new Meat();
casings = new Casings();
sausage = new Sausage(casings, meat);
seeYouTomrrow = new SeeYouTomrrow();
spicyHotPot = new SpicyHotPot(potato, doufu, sausage, seeYouTomrrow, fishBall,beef);
spicyHotPot.eat();
}
}
複製代碼
這裏就是初始化了不少對象,其中有些對象中還引用了其餘對象,像這樣的初始化代碼在日常開發中仍是比較常見的,而使用了Dagger2
就能夠這樣寫:
public class TestActivity extends AppCompatActivity {
@Inject
SpicyHotPot spicyHotPot;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
DaggerTestComponent.builder().build().inject(this);
spicyHotPot.eat();
}
}
複製代碼
使用了Dagger2
是否是簡單了很多,只要一個註解加一行代碼,就完成了多個對象的初始化,並且不管對象如何修改,這裏的代碼都無需變更,完成了解耦。
既然Dagger2
是經過註解實現的依賴注入,那麼學習使用Dagger2
就是要學習Dagger2
中的註解的使用。不過,在具體看Dagger2
中的註解以前,先要在項目中引入Dagger2
的依賴,按照Github
上Dagger2的文檔引入:
dependencies {
implementation 'com.google.dagger:dagger:2.22.1'
annotationProcessor 'com.google.dagger:dagger-compiler:2.22.1'
}
複製代碼
接下來來看Dagger2
具體的註解。
@Inject
註解的做用主要有兩個:
因此咱們要使用Dagger2
初始化一個類的依賴首先要在這個類的構造函數上加上@Inject
註解,而後在須要依賴的地方的對應變量上也加上@Inject
註解。可是光加上@Inject註解完成所謂的依賴注入嗎?答案是否認的,@Inject
只是標註了依賴的需求方和依賴的提供方,可是它們倆之間尚未創建關係橋樑。而@Component
就是幹這個的,具體來看下面這個例子:
public class Cola {
@Inject
public Cola() {
}
public String returnName() {
return "百事可樂";
}
}
複製代碼
先定義一個可樂類,在他的構造函數上加上@Inject
註解,表示提供該類的依賴。再在Activity
中定義一個Cola
類型變量一樣加上@Inject
註解,表示須要該類依賴:
public class InjectAndComponentActivity extends AppCompatActivity {
private TextView mTextContent;
@Inject
Cola cola;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_inject_and_component);
mTextContent = findViewById(R.id.textContent);
}
}
複製代碼
接着新建一個接口ColaComponent
:
@Component
public interface ColaComponent {
void inject(InjectAndComponentActivity injectAndComponentActivity);
}
複製代碼
接口上標註了@Component
註解,接着點擊AndroidStudio上的Make Project
編譯項目,此時Dagger2
會自動生成這個接口的實現類DaggerColaComponent
,由這個實現類的方法來完成依賴注入。接着在Activity
中經過實現調用他的inject
方法完成注入。
public class InjectAndComponentActivity extends AppCompatActivity {
private TextView mTextContent;
@Inject
Cola cola;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_inject_and_component);
mTextContent = findViewById(R.id.textContent);
DaggerColaComponent.builder().build().inject(this);
mTextContent.setText(cola.returnName());
}
}
複製代碼
注入以後就能夠調用該對象的方法,運行效果:
上面舉的麻辣燙的例子的實現也是同樣的,每一個食材類的構造函數上加了@Inject
註解,建立一個Component
接口。貼上部分代碼:
//腸衣
public class Casings {
@Inject
public Casings() {
}
}
//肉
public class Meat {
@Inject
public Meat() {
}
}
//香腸
public class Sausage {
Casings casings;
Meat meat;
@Inject
public Sausage(Casings casings, Meat meat) {
this.casings = casings;
this.meat = meat;
}
}
//麻辣燙
public class SpicyHotPot {
Potato potato;
Doufu doufu;
Sausage sausage;
SeeYouTomorrow seeYouTomorrow;
FishBall fishBall;
Beef beef;
@Inject
public SpicyHotPot(Potato potato, Doufu doufu, Sausage sausage, SeeYouTomorrow seeYouTomorrow, FishBall fishBall, Beef beef) {
this.potato = potato;
this.doufu = doufu;
this.sausage = sausage;
this.seeYouTomorrow = seeYouTomorrow;
this.fishBall = fishBall;
this.beef = beef;
}
public void eat() {
Log.d("Dagger2", "我開動了");
}
}
複製代碼
不管是3.1中Cola
類仍是以前的各類食材類都是咱們本身定義的類,因此能夠本身修改,想使用Dagger2
只須要在類的構造函數上加上@Inject
註解。可是實際開發中會遇到使用三方類庫的狀況,這些三方類庫中的類代碼咱們沒法修改,無法在其構造函數上加@Inject註解,那麼是否是無法使用Dagger2
了呢?答案仍是否認的,Dagger2
中的@Module
和@Provides
註解就是用來處理只種狀況。
看下面這個例子,這回不吃麻辣燙了,來吃炸雞,因而定義了一個德克士類。
public class Dicos {
String friedDrumstick;
public Dicos() {
friedDrumstick = "脆皮手槍腿";
}
public String returnDicos() {
return "德克士:" + friedDrumstick;
}
}
複製代碼
這回不添加任何註解,就是一個正常的Dicos
對象類。接着一樣新建一個DicosComponent
接口:
@Component
public interface DicosComponent {
void inject(ModuleAndProvidesActivity moduleAndProvidesActivity);
}
複製代碼
接着在Activity
中定義Dicos
類型變量:
public class ModuleAndProvidesActivity extends AppCompatActivity {
private TextView mTextContent;
@Inject
Dicos dicos;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_module_and_provides);
mTextContent = findViewById(R.id.textContent);
}
}
複製代碼
至此爲止都和以前是同樣的,接下來新建一個DicosModule
類,由於無法在三方類的構造函數上加@Inject
註解,因此要經過Module
類來提供依賴。
@Module
public class DicosModule {
@Provides
public Dicos getDicos() {
return new Dicos();
}
}
複製代碼
這裏首先建立了一個DicosModule
類,並在DicosModule
類上加上@Module
註解,接着在這個類中只寫了一個getDicos()
方法,調用Dicos
的構造方法建立對象而後返回。經過在方法上加上@Provides
註解,表示由這個方法爲Dicos
類型提供依賴。最後記得還要在DicosComponent
接口註解上加上DicosModule
。這樣在Activity
中@Inject
標記須要依賴時,才能找到。
@Component(modules = DicosModule.class)
public interface DicosComponent {
void inject(ModuleAndProvidesActivity moduleAndProvidesActivity);
}
複製代碼
Make Project
後在Activity
中一樣調用DaggerDiscosComponent
的inject
方法注入依賴便可。
public class ModuleAndProvidesActivity extends AppCompatActivity {
private TextView mTextContent;
@Inject
Dicos dicos;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_module_and_provides);
mTextContent = findViewById(R.id.textContent);
DaggerDicosComponent.builder().dicosModule(new DicosModule()).build().inject(this);
mTextContent.setText(dicos.returnDicos());
}
}
複製代碼
運行結果:
接下來考慮一下這種狀況,若是一個類有多個構造方法,或者有兩個相同依賴時,它們都繼承同一個父類或者實現同一個接口,那麼怎麼區分呢?這就要用到@Named
或者@Qualifier
註解了。此次定義一個小肥羊火鍋類。
public class LittleSheep {
String mutton = "沒有肉";
String vegetables = "沒有菜";
public LittleSheep(String mutton) {
this.mutton = mutton;
}
public LittleSheep(String mutton, String vegetables) {
this.mutton = mutton;
this.vegetables = vegetables;
}
public String retrunCotent() {
return "小肥羊火鍋:" + mutton + " " + vegetables;
}
}
複製代碼
這個類有兩個構造函數,接着一樣是建立Component
和Module
類。
@Component(modules = LittleSheepModule.class)
public interface LittleSheepComponent {
void inject(NamedActivity namedActivity);
}
複製代碼
@Module
public class LittleSheepModule {
@Named("all")
@Provides
public LittleSheep provideLittleSheepAll() {
return new LittleSheep("十盤羊肉", "一盤蔬菜");
}
@Named("mutton")
@Provides
public LittleSheep provideLittleSheepSingle() {
return new LittleSheep("二十盤羊肉吃個夠");
}
}
複製代碼
LittleSheepComponent
和以前的沒什麼區別,看到LittleSheepModule
,這個Module
有兩個@Provides
註解方法,分別對應兩個不一樣傳參的構造函數,可是這兩個方法返回類型都是LittleSheep
都提供同一類型的依賴,在需求依賴的時候Dagger2
就分不清是哪一個了,因此這裏要用@Named
註解來作個區分,這裏分別設置兩個不一樣的name
,all
和mutton
作區分。除此以外在Activity
中,需求依賴的時候也要使用@Named
作區分。
public class NamedActivity extends AppCompatActivity {
private TextView mTextContent;
@Named("all")
@Inject
LittleSheep allLittleSheep;
@Named("mutton")
@Inject
LittleSheep littleSheep;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_named);
mTextContent = findViewById(R.id.textContent);
DaggerLittleSheepComponent.builder().build().inject(this);
mTextContent.setText(allLittleSheep.retrunCotent() + "\n" + littleSheep.retrunCotent());
}
}
複製代碼
NamedActivity
中一樣在@Inject
註解上還要加上@Named
區分。運行結果:
接下來看繼承同一個父類的狀況,首先定義一個快餐類FastFood
,再定義他的兩個子類KFC
和BurgerKing
。
public abstract class FastFood {
public abstract String returnContent() ;
}
複製代碼
public class KFC extends FastFood {
public KFC() {
}
@Override
public String returnContent() {
return "KFC全家桶";
}
}
複製代碼
public class BurgerKing extends FastFood {
String beefBurger = "三層牛肉漢堡";
public BurgerKing() {
}
@Override
public String returnContent() {
return "漢堡王:" + beefBurger;
}
}
複製代碼
接着仍是建立Component
和Module
類。
@Component(modules = FastFoodQualifierModule.class)
public interface FastFoodQualifierComponent {
void inject(FastFoodQualifierActivity fastFoodQualifierActivity);
}
複製代碼
@Module
public class FastFoodQualifierModule {
@Provides
public FastFood getKFC() {
return new KFC();
}
@Provides
public FastFood getBurgerKing() {
return new BurgerKing();
}
}
複製代碼
主要的問題仍是在Module
中,這樣寫仍是沒法區分究竟是KFC
仍是BurgerKing
的依賴。這回不使用@Named
使用@Qualifier
處理。@Qualifier
比@Named
更加靈活和強大,用於自定義註解。接下來咱們定義兩個註解。
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface KFC {
}
複製代碼
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface BurgerKing {
}
複製代碼
一個KFC
和一個BurgerKing
註解,存活時長都選擇RUNTIME
,再添加上@Qualifier
註解限定,接下來就能夠用這兩個自定義註解來區分Module
中方法的類型。
@Module
public class FastFoodQualifierModule {
@com.sy.dagger2demo.annotations.KFC
@Provides
public FastFood getKFC() {
return new KFC();
}
@com.sy.dagger2demo.annotations.BurgerKing
@Provides
public FastFood getBurgerKing() {
return new BurgerKing();
}
}
複製代碼
FastFoodQualifierModule
中兩個方法上分別添加剛纔的兩個註解,接下來Activity
中和使用@Named
並沒什麼不一樣,只是把@Named
註解分別換成剛纔定義的註解。
public class FastFoodQualifierActivity extends AppCompatActivity {
private TextView mTextContent;
@KFC
@Inject
FastFood kfc;
@BurgerKing
@Inject
FastFood burgerKing;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fast_food_qualifier);
mTextContent = (TextView) findViewById(R.id.textContent);
DaggerFastFoodQualifierComponent.builder().build().inject(this);
mTextContent.setText(kfc.returnContent()+"\n"+burgerKing.returnContent());
}
}
複製代碼
運行結果:
@Singleton
看名字就知道這個註解是用來實現單例的。再次定義一個NetworkClient
類。採用@Module
和@Provides
註解實現依賴注入。
public class NetworkClient {
String baseUrl;
public NetworkClient() {
baseUrl = "http://www.baidu.com/";
}
public void init() {
Log.d("NetworkClient", "網絡初始化");
}
}
複製代碼
@Component(modules = NetworkModule.class)
public interface NetworkComponent {
void inject(SingletonAndScopeActivity singletonAndScopeActivity);
}
複製代碼
@Module
public class NetworkModule {
@Provides
public NetworkClient getClient() {
return new NetworkClient();
}
}
複製代碼
public class SingletonAndScopeActivity extends AppCompatActivity {
private TextView mContentTextView;
@Inject
NetworkClient networkClient1;
@Inject
NetworkClient networkClient2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_singleton_and_scope);
mContentTextView = (TextView) findViewById(R.id.contentTextView);
DaggerNetworkComponent.create().inject(this);
mContentTextView.setText(networkClient1.toString() + "\n" + networkClient2.toString());
}
}
複製代碼
這裏的代碼和以前的徹底相同,只是在Activity
中添加了一個對象,同時有兩個NetworkClient
對象,調用hashCode
方法查看是否爲同一個對象。 運行結果:
能夠看到這裏hashCode
不一樣,明顯是兩個對象。接下來使用@Singleton
實現單例。首先在NetworkComponent
上加上@Singleton
註解:
@Singleton
@Component(modules = NetworkModule.class)
public interface NetworkComponent {
void inject(SingletonAndScopeActivity singletonAndScopeActivity);
}
複製代碼
接着在NetworkModule
中的方法上也加上@Singleton
註解:
@Module
public class NetworkModule {
@Singleton
@Provides
public NetworkClient getClient() {
return new NetworkClient();
}
}
複製代碼
這樣就ok了,就是這麼簡單,再次運行程序查看hashCode
。這時hashCode
相同已是同一個對象了。
運行結果:
注意這裏其實@Singleton
實現的單例只是在同個Activity
下的單例,在其餘Activity
下,再次建立這了類的對象就再也不是同一個對象了。這裏咱們在新建一個Activity
測試下:
public class SecondActivity extends AppCompatActivity {
private TextView mContentTextView;
@Inject
NetworkClient networkClient;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
mContentTextView = (TextView) findViewById(R.id.contentTextView);
DaggerNetworkComponent.create().inject(this);
mContentTextView.setText(networkClient.toString());
}
}
複製代碼
在Component
中添加inject
方法類型爲SecondActivity
:
@Singleton
@Component(modules = NetworkModule.class)
public interface NetworkComponent {
void inject(SingletonAndScopeActivity singletonAndScopeActivity);
void inject(SecondActivity secondActivity);
}
複製代碼
運行結果:
能夠看到雖然使用了@Singleton
註解,可是也只能保證在同一個Activity
中是單例同一個對象,在多個Activity
中就沒法保證了。那麼怎麼實現全局的單例呢?這能夠用@Scope
註解。
@Scope
一樣用來自定義註解用來限定註解,由於咱們知道Application
是單例的,因此可使用@Scope
結合Application
實現全局的單例模式。先定義一個新註解ApplicationScope
。
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface ApplicationScope {
}
複製代碼
這個和上面相似,只是用了@Scope
註解,接着修改以前的NetworkModule
,將@Singleton
換成新註解@ApplicationScope
:
@Module
public class NetworkModule {
@ApplicationScope
@Provides
public NetworkClient getClient() {
return new NetworkClient();
}
}
複製代碼
接着建立一個新的ActivityComponent
:
@ApplicationScope
@Component(modules = NetworkModule.class)
public interface ActivityComponent {
void inject(SingletonAndScopeActivity singletonAndScopeActivity);
void inject(SecondActivity secondActivity);
}
複製代碼
這裏一樣是使用了@ApplicationScope
註解,接着建立MyApplicatin
類在其中去獲取ActivityComponent
的實例。
public class MyApplication extends Application {
ActivityComponent activityComponent;
@Override
public void onCreate() {
super.onCreate();
activityComponent = DaggerActivityComponent.builder().build();
}
public static MyApplication getApplication(Context context){
return (MyApplication) context.getApplicationContext();
}
public ActivityComponent getActivityComponent(){
return activityComponent;
}
}
複製代碼
這裏看到在MyApplication
中經過Dagger2
獲取到ActivityComponet
的實例再給出public
方法獲取這個ActivityComponent
實例。這樣在Activity
中就能夠經過這個ActivityComponent
注入,得到單例的對象了。修改Activity
中代碼:
public class SingletonAndScopeActivity extends AppCompatActivity {
private TextView mContentTextView;
@Inject
NetworkClient networkClient1;
@Inject
NetworkClient networkClient2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_singleton_and_scope);
mContentTextView = (TextView) findViewById(R.id.contentTextView);
// DaggerNetworkComponent.create().inject(this);
//經過Application中的Component注入
MyApplication.getApplication(this).getActivityComponent().inject(this);
mContentTextView.setText(networkClient1.hashCode() + "\n" + networkClient2.hashCode());
mContentTextView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(SingletonAndScopeActivity.this,SecondActivity.class));
}
});
}
}
複製代碼
public class SecondActivity extends AppCompatActivity {
private TextView mContentTextView;
@Inject
NetworkClient networkClient;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
mContentTextView = (TextView) findViewById(R.id.contentTextView);
// DaggerNetworkComponent.create().inject(this);
//經過Application中的Component注入
MyApplication.getApplication(this).getActivityComponent().inject(this);
mContentTextView.setText(networkClient.hashCode()+"");
}
}
複製代碼
運行結果:
再次運行查看結果,發現這是已經全是同一個對象了。
Component
還能夠經過dependencies
依賴於別的Component
。這裏再從新定義一個PizzaHut
類:
public class PizzaHut {
String SuperSupremePizza = "超級至尊披薩";
public PizzaHut() {
}
public String returnContent() {
return "必勝客超級至尊套餐:" + SuperSupremePizza;
}
}
複製代碼
@Module
public class PizzaHutModule {
@Provides
public PizzaHut getPizzaHut() {
return new PizzaHut();
}
}
複製代碼
@Component(modules = PizzaHutModule.class)
public interface PizzaHutComponent {
void inject(SingletonAndScopeActivity singletonAndScopeActivity);
void inject(SecondActivity secondActivity);
}
複製代碼
同時還建立了對應的Module
和Componet
,仍是和以前沒什麼區別。接下來在ActivityComponent
中使用dependencies
將PizzaHutComponent
引入。
@ApplicationScope
@Component(modules = NetworkModule.class,dependencies = PizzaHutComponent.class)
public interface ActivityComponent {
void inject(SingletonAndScopeActivity singletonAndScopeActivity);
void inject(SecondActivity secondActivity);
}
複製代碼
接着到MyApplication
中引入PizzaHutComponent
:
public class MyApplication extends Application {
ActivityComponent activityComponent;
@Override
public void onCreate() {
super.onCreate();
activityComponent = DaggerActivityComponent.builder().pizzaHutComponent(DaggerPizzaHutComponent.builder().build()).build();
}
public static MyApplication getApplication(Context context) {
return (MyApplication) context.getApplicationContext();
}
public ActivityComponent getActivityComponent() {
return activityComponent;
}
}
複製代碼
最後再到剛纔的Activity
中添加一個PizzaHut
類型變量:
public class SingletonAndScopeActivity extends AppCompatActivity {
private TextView mContentTextView;
@Inject
NetworkClient networkClient1;
@Inject
NetworkClient networkClient2;
@Inject
PizzaHut pizzaHut;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_singleton_and_scope);
mContentTextView = (TextView) findViewById(R.id.contentTextView);
// DaggerNetworkComponent.create().inject(this);
MyApplication.getApplication(this).getActivityComponent().inject(this);
mContentTextView.setText(networkClient1.hashCode() + "\n" + networkClient2.hashCode()+"\n"+pizzaHut.returnContent());
mContentTextView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(SingletonAndScopeActivity.this,SecondActivity.class));
}
});
}
}
複製代碼
運行結果:
能夠看到這裏已經成功注入PizzaHut
,而且調用了returnContent
方法。
Dagger2
也支持懶加載模式,就是@Inject
的時候不初始化,而到使用的時候調用get
方法獲取實例。
public class SingletonAndScopeActivity extends AppCompatActivity {
private TextView mContentTextView;
@Inject
NetworkClient networkClient1;
@Inject
NetworkClient networkClient2;
@Inject
Lazy<PizzaHut> pizzaHut;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_singleton_and_scope);
mContentTextView = (TextView) findViewById(R.id.contentTextView);
// DaggerNetworkComponent.create().inject(this);
MyApplication.getApplication(this).getActivityComponent().inject(this);
//使用的時候調用get方法獲取處理
mContentTextView.setText(networkClient1.hashCode() + "\n" + networkClient2.hashCode()+"\n"+pizzaHut.get().returnContent());
mContentTextView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(SingletonAndScopeActivity.this,SecondActivity.class));
}
});
}
}
複製代碼
在對Dagger2
中的註解有了必定的瞭解以後,繼續學習Dagger2+MVP
。將Dagger2
與MVP
結構結合起來,可使MVP
架構中的依賴更加清晰更加易於管理。學習MVP+Dagger2
天然是去看Google
官方提供的Demo
。
先看一下工程目錄:
MVP
的
Presenter
、
Contract
、
Fragment
等類以外,還有
Component
和
Module
這些類都是使用了
Dagger2
會用到的。接着先來具體看
ToDoApplication
類:
public class ToDoApplication extends Application {
private TasksRepositoryComponent mRepositoryComponent;
@Override
public void onCreate() {
super.onCreate();
mRepositoryComponent = DaggerTasksRepositoryComponent.builder()
.applicationModule(new ApplicationModule((getApplicationContext())))
.tasksRepositoryModule(new TasksRepositoryModule()).build();
}
public TasksRepositoryComponent getTasksRepositoryComponent() {
return mRepositoryComponent;
}
}
複製代碼
看到這裏就是構建了一個TasksRepositoryComponent
,並提供了一個得到的方法。先找到TaskRepositoryComponent
:
@Singleton
@Component(modules = {TasksRepositoryModule.class, ApplicationModule.class})
public interface TasksRepositoryComponent {
TasksRepository getTasksRepository();
}
複製代碼
首先看到這裏用了單例的註解,接着看到有兩個Module
,還提供了一個獲取TaskRepository
的方法,這個TaskRepository
是用來獲取數據的,在Presenter
構造中傳入,Presenter
調用其中方法得到數據。先來看TaskRepositoryModule
:
@Module
public class TasksRepositoryModule {
@Singleton
@Provides
@Local
TasksDataSource provideTasksLocalDataSource(Context context) {
return new TasksLocalDataSource(context);
}
@Singleton
@Provides
@Remote
TasksDataSource provideTasksRemoteDataSource() {
return new FakeTasksRemoteDataSource();
}
}
複製代碼
看到這個Module
中有兩個@Provides
標註的方法,是用來提供測試數據返回的。一個方法是本地數據,一個是模擬遠程數據。返回的都是TasksDataSource類型,因此用了自定義註解@Local
和@Remote
作了區分。點進去看這兩個註解,其定義時都用了@Qualifier
註解。
@Qualifier
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface Local {
}
@Qualifier
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface Remote {
}
複製代碼
接着返回看ApplicationModule
:
@Module
public final class ApplicationModule {
private final Context mContext;
ApplicationModule(Context context) {
mContext = context;
}
@Provides
Context provideContext() {
return mContext;
}
}
複製代碼
看到其中只提供了一個上下文的mContext
方法。回到ToDoApplication
中看到這裏建立ApplicationModule
傳入的是ApplicationContext
。下面進入tasks
這個具體模塊頁面查看Dagger2
具體是怎麼和MVP
結合的。
public class TasksActivity extends AppCompatActivity {
private static final String CURRENT_FILTERING_KEY = "CURRENT_FILTERING_KEY";
private DrawerLayout mDrawerLayout;
@Inject TasksPresenter mTasksPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.tasks_act);
......
TasksFragment tasksFragment =
(TasksFragment) getSupportFragmentManager().findFragmentById(R.id.contentFrame);
if (tasksFragment == null) {
// Create the fragment
tasksFragment = TasksFragment.newInstance();
ActivityUtils.addFragmentToActivity(
getSupportFragmentManager(), tasksFragment, R.id.contentFrame);
}
// Create the presenter
DaggerTasksComponent.builder()
.tasksRepositoryComponent(((ToDoApplication) getApplication()).getTasksRepositoryComponent())
.tasksPresenterModule(new TasksPresenterModule(tasksFragment)).build()
.inject(this);
......
}
......
}
複製代碼
這段是TasksActivity
中的代碼,其中省略掉了一些無關的代碼。能夠看到這和Google
官方MVP
的Demo
同樣,是在Activity
裏放了一個Fragment
將Fragment
做爲View
使用。看到這裏在TasksPresenter
上加了@Inject
註解,也就是說這裏是要用Dagger2
初始化Presenter
。在onCreate
方法中經過DaggerTasksComponent
的inject
方法注入TasksPresenter
依賴,建立TasksPresenter
。接下來來看TasksComponent
接口的代碼:
@FragmentScoped
@Component(dependencies = TasksRepositoryComponent.class, modules = TasksPresenterModule.class)
public interface TasksComponent {
void inject(TasksActivity activity);
}
複製代碼
其中除了設置了TasksPresenterModule
並且還依賴了TasksRepositoryComponent
這個Component
。這就讓以前的TasksRepository
在這裏也可使用。接着進入TasksPresenterModule
查看:
@Module
public class TasksPresenterModule {
private final TasksContract.View mView;
public TasksPresenterModule(TasksContract.View view) {
mView = view;
}
@Provides
TasksContract.View provideTasksContractView() {
return mView;
}
}
複製代碼
TasksPresenterModule
中標註了mView
的@Provides
方法,爲注入View
提供了方法。最後看到TasksPresenter
:
final class TasksPresenter implements TasksContract.Presenter {
private final TasksRepository mTasksRepository;
private final TasksContract.View mTasksView;
private TasksFilterType mCurrentFiltering = TasksFilterType.ALL_TASKS;
private boolean mFirstLoad = true;
@Inject
TasksPresenter(TasksRepository tasksRepository, TasksContract.View tasksView) {
mTasksRepository = tasksRepository;
mTasksView = tasksView;
}
@Inject
void setupListeners() {
mTasksView.setPresenter(this);
}
......
}
}
複製代碼
TasksPresenter
的構造方法上加上@Inject
註解提供了依賴。至此全部對象具能由Dagger2
提供依賴,在TaskActivity
注入依賴,完成了Dagger2
與MVP
的結合,完成了解耦。
DaggerTasksComponent.builder()
.tasksRepositoryComponent(((ToDoApplication) getApplication()).getTasksRepositoryComponent())
.tasksPresenterModule(new TasksPresenterModule(tasksFragment)).build()
.inject(this);
複製代碼
1. 使用Dagger2的目的是解耦。使用Dagger2更好更清晰地管理項目中類之間的依賴。
2. 關於Dagger2的使用個人理解是: