1,在做項目時,經常需要在一個對象里去創建另一個對象的示例,這種行為是產生耦合的常見形式,對於一個大型項目來說,過多的相互依賴會導致代碼難以維護,很容易就會碰到修改一個小需求需要大面積的修改各種代碼,如果代碼還不是自己寫的話,那基本上就是在問候上一個同事的親人了。再來看看我們今天要了解的Dagger2它的作用是什么,來吧,先來一頓吹比誇獎:依賴注入框架主要用於模塊間解耦,提高代碼的健壯性和可維護性。Dagger 這個庫的取名不僅僅來自它的本意“匕首”,同時也暗示了它的原理。
2,我們來一一個簡單的顯示場景來引入吧,昨天小弟剛剛到了一個小縣城,去拜訪了幾個老同學,順便從他們哪兒biang來了一些開公司的錢,搞了個小小的咖啡店CoffeeShop,現在要通過我們的咖啡機CoffeeMachine來生產咖啡,比較嫌我這么窮的窮人已經很少見了,編成也編出不了一個北京的廁所來,必須要來賺外快錢生活了,比較生活是要繼續的,嗯,現在我們來生產咖啡。
CoffeeMachine.java
package com.qianmo.daggercoffee.bean; /** * Created by wangjitao on 2016/11/2 0002. * 現實的場景我們有一家咖啡機 * 現在模擬一下咖啡機制造咖啡的過程 */ public class CoffeeMachine { private CoffeeMaker maker; public CoffeeMachine() { maker = new SimpleMaker(); } public String makeCoffee() { return maker.makeCoffee(); } }
這里要先說一下,生產咖啡有很多方式,這里我們為了體驗java的多態性,先創建一個CoffeeMaker的的接口,然后通過SimpleMaker就是我們簡單的通過咖啡機生產了
CoffeeMaker.java
package com.qianmo.daggercoffee.bean; /** * Created by wangjitao on 2016/11/2 0002. * 這是咖啡制造者,提供一個制造方法 */ public interface CoffeeMaker { String makeCoffee(); }
SimpleMaker.java
package com.qianmo.daggercoffee.bean; /** * Created by wangjitao on 2016/11/2 0002. * 這里創建一個簡單的制造方法 */ public class SimpleMaker implements CoffeeMaker { @Override public String makeCoffee() { return "Coffee is made by SimperMarker"; } }
好了,這樣我們就可以開開心心的賣我們的咖啡了,時間流逝,由於小鎮不是很發達,鎮上的唯一一家咖啡店生意日漸火爆,阿呆哥哥笑呵呵的在哪兒坐着數錢,心里面並感嘆着“坐着數錢的日子可真爽啊”,可是新的問題也來了,客戶覺得咖啡機生產口味太單調了,強烈推薦本哥哥招一名咖啡師,按照客戶喜歡的口味來搭配着做咖啡,阿呆哥哥還在IT的菜鳥世界遨游着,畢竟是菜鳥,對於來的需求都是滿臉笑容的打贏下來,然后再在之后的通宵加班里面問候產品的親人。經過公司董事長阿呆哥哥激烈的和各位董事會的股東深刻的分析(就是自己坐在門前自言自語一個多小時),決定斥重資解決這個問題!!!
先來創建我們的咖啡師
Cooker.java
package com.qianmo.daggercoffee.bean; /** * Created by wangjitao on 2016/11/2 0002. * 根據需求添加的咖啡師 */ public class Cooker { private String name; //咖啡師的姓名 private String coffeeKind; //制作咖啡的類型 public Cooker(String name, String coffeeKind) { this.name = name; this.coffeeKind = coffeeKind; } public String make() { return name + " make " + coffeeKind; //咖啡師制作咖啡 } }
這時候我們招來了一個咖啡師,相應的生產的方式SimpleMaker也要變了
SimpleMaker.java
package com.qianmo.daggercoffee.bean; /** * Created by wangjitao on 2016/11/2 0002. * 這里創建一個簡單的制造方法 */ public class SimpleMaker implements CoffeeMaker { Cooker cooker ; //添加咖啡師來制作咖啡 public SimpleMaker(Cooker cooker){ this.cooker = cooker ; } @Override public String makeCoffee() { return cooker.make(); } }
相應的我們的咖啡機也要改變一下,現在是專業的咖啡師來操作,不再是阿呆哥哥這個編成的,所以現在要修改成
CoffeeMachine.java
package com.qianmo.daggercoffee.bean; /** * Created by wangjitao on 2016/11/2 0002. * 現實的場景我們有一家咖啡機 * 現在模擬一下咖啡機制造咖啡的過程 */ public class CoffeeMachine { private CoffeeMaker maker; public CoffeeMachine(Cooker cooker) { maker = new SimpleMaker(cooker); } public String makeCoffee() { return maker.makeCoffee(); } }
這時候我們的咖啡機就不樂意了,你升級就升級唄,還要我修改東西,難不成把蘇寧的設計師叫來修改一下生產的條件,很明顯,這時候以一個程序員的角度,我們這個需求是有問題的。但是客戶是上帝啊,不能又不招人吧,怎么辦呢,真的是把蘇寧的設計師搞過來? 作為在技術部里面敲代碼的阿呆哥哥此刻陷入了深深的沉思,是晚上去和產品經理撿肥皂還是直接那鍵盤去客戶那邊跪在那里。什么都不管了,先抽一根煙開心開心,不竟陷入了深深的沉思。
這時候作為程序猿的阿呆哥哥就仔細的分析了一波,發現主要是這個構造函數,只要把這個搞定了不就可以了,依賴注入,好嘞,看一下我們下面的代碼:
InjectMaker.java
package com.qianmo.daggercoffee.bean; /** * Created by wangjitao on 2016/11/2 0002. */ public interface InjectMaker { void injectMaker(CoffeeMaker maker) ; }
再看看替換CoffeeMachine的CoffeeMachineWithInjection類
CoffeeMachineWithInjection.java
package com.qianmo.daggercoffee.bean; /** * Created by wangjitao on 2016/11/2 0002. */ public class CoffeeMachineWithInjection implements InjectMaker { private CoffeeMaker maker; //常見依賴注入的三種形式 /** * 1,構造函數注入 */ public CoffeeMachineWithInjection(CoffeeMaker maker) { this.maker = maker; } /** * 2,set注入 */ public void setMaker(CoffeeMaker maker) { this.maker = maker; } /** * 3,接口注入 */ @Override public void injectMaker(CoffeeMaker maker) { this.maker = maker; } public String makeCoffee() { return maker.makeCoffee(); } }
這里我們采用第三種方法,接口注入,這樣的話我們就完全不怕客戶提供需求了。一切都變得如此美好,可以開開心心的找老板加雞腿了。
ok,在項目中如何防止這樣的事情發生呢,很簡單,這時候我們得使用Dagger2了!
3,Dagger2的使用
引入:
buildscript { repositories { jcenter() } dependencies { classpath 'com.android.tools.build:gradle:2.1.0' classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' } } apply plugin: 'com.neenbedankt.android-apt' dependencies { apt 'com.google.dagger:dagger-compiler:2.0' compile 'com.google.dagger:dagger:2.0' }
看過Dagger2的人應該都知道,Dagger2是通過注解來實現依賴注入的,所以還沒了解過注解的同學可以先去google一下,下面先介紹一下它的四個基本
四個基本
這里說的四個基礎,指的是四種基礎的注解,他們分別是:
@Inject Inject主要有兩個作用,一個是使用在構造函數上,通過標記構造函數讓Dagger2來使用(Dagger2通過Inject標記可以在需要這個類實例的時候來找到這個構造函數並把相關實例new出來)從而提供依賴,另一個作用就是標記在需要依賴的變量讓Dagger2為其提供依賴。
@Provide 用Provide來標注一個方法,該方法可以在需要提供依賴時被調用,從而把預先提供好的對象當做依賴給標注了@Injection的變量賦值。provide主要用於標注Module里的方法
@Module 用Module標注的類是專門用來提供依賴的。有的人可能有些疑惑,看了上面的@Inject,需要在構造函數上標記才能提供依賴,那么如果我們需要提供的類構造函數無法修改怎么辦,比如一些jar包里的類,我們無法修改源碼。這時候就需要使用Module了。Module可以給不能修改源碼的類提供依賴,當然,能用Inject標注的通過Module也可以提供依賴
@Component Component一般用來標注接口,被標注了Component的接口在編譯時會產生相應的類的實例來作為提供依賴方和需要依賴方之間的橋梁,把相關依賴注入到其中。
讓我們在代碼中來實現一下
package com.qianmo.daggercoffee; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.Button; import android.widget.TextView; import com.qianmo.daggercoffee.bean.CoffeeMachine; import com.qianmo.daggercoffee.bean.Cooker; public class MainActivity extends AppCompatActivity implements View.OnClickListener { TextView tvCoffee; Button btnMake; CoffeeMachine coffeeMachine; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); tvCoffee = (TextView) findViewById(R.id.tv_coffee); btnMake = (Button) findViewById(R.id.btn_make); btnMake.setOnClickListener(this); Cooker cooker = new Cooker("wangjitao", "Es"); coffeeMachine = new CoffeeMachine(cooker); } private String makeCoffee() { return coffeeMachine.makeCoffee(); } @Override public void onClick(View view) { tvCoffee.setText(makeCoffee()); } }
這是我們以前的寫法,使用new對象的方式來獲取想要的對象,再看看我們使用Dagger2來怎么注入依賴
首先來分析一下,很明顯第一層我們需要一個CoffeeMachine對象,所以在activity中我們要這樣寫這樣的代碼
@Inject CoffeeMachine coffeeMachine;
告訴Dagger2我們這里是需要對象的,所以這里需要注入依賴,那下面的問題來了,我們的Dagger2怎么知道它去哪找這個對象呢?
上面提到過,提供依賴可以給相關依賴類的構造函數添加@Inject注解,這樣Dagger2就能找到它並用來提供依
先看看原先的CoffeeMachine
package com.qianmo.daggercoffee.bean; /** * Created by wangjitao on 2016/11/2 0002. * 現實的場景我們有一家咖啡機 * 現在模擬一下咖啡機制造咖啡的過程 */ public class CoffeeMachine { private CoffeeMaker maker; public CoffeeMachine(Cooker cooker) { maker = new SimpleMaker(cooker); } public String makeCoffee() { return maker.makeCoffee(); } }
再看看添加了Dagger的代碼
package com.qianmo.daggercoffee.bean; import javax.inject.Inject; /** * Created by wangjitao on 2016/11/2 0002. * 現實的場景我們有一家咖啡機 * 現在模擬一下咖啡機制造咖啡的過程 */ public class CoffeeMachine { private CoffeeMaker maker; @Inject public CoffeeMachine(Cooker cooker) { maker = new SimpleMaker(cooker); } public String makeCoffee() { return maker.makeCoffee(); } }
很明顯,我們只是在CoffeeMachine的構造函數添加了一個@Inject,這樣我們的Dagger2就知道在哪里去拿對象提供者了,但是感覺有點不對啊,我們的構造中好像還new了一個SimpleMaker的對象啊,想我們有強迫症的人是絕對不允許的,所以,先修改CoffeeMachine的代碼
package com.qianmo.daggercoffee.bean; import javax.inject.Inject; /** * Created by wangjitao on 2016/11/2 0002. * 現實的場景我們有一家咖啡機 * 現在模擬一下咖啡機制造咖啡的過程 */ public class CoffeeMachine { private CoffeeMaker maker; @Inject public CoffeeMachine(CoffeeMaker maker) { this.maker = maker; } public String makeCoffee() { return maker.makeCoffee(); } }
再來修改SimpleMaker
import javax.inject.Inject; /** * Created by wangjitao on 2016/11/2 0002. * 這里創建一個簡單的制造方法 */ public class SimpleMaker implements CoffeeMaker { // /** // * 剛創建的時候的方式 // * @return // */ // @Override // public String makeCoffee() { // return "Coffee is made by SimperMarker"; // } Cooker cooker; //添加咖啡師來制作咖啡 @Inject public SimpleMaker(Cooker cooker) { this.cooker = cooker; } @Override public String makeCoffee() { return cooker.make(); } }
這次變聰明了,SimpleMaker的構造函數中Cooker的依賴也是注入進來的,這個時候我們還需要提供Cooker的依賴
but,先打住一下,我們前面說了,除了構造函數提供依賴,還能用Module提供,所以,機智的我們這次用Module來提供依賴。
這里我們創建一個新的Module
SimpleModule.java
package com.qianmo.daggercoffee.bean; import dagger.Module; import dagger.Provides; /** * Created by Administrator on 2016/11/3 0003. */ @Module public class SimpleModule { @Provides Cooker provideCooker() { return new Cooker("gao", "noma"); } }
好了 ,這里我們把需要依賴的東西都寫好了,現在就是怎么讓創建好的對象能在需要它的地方出現,來看看下面這個圖
根據上面的圖可以看到,很明顯我們缺少一個注射器,在Dagger2中,注射器就是我們的Component
package com.qianmo.daggercoffee.bean; import com.qianmo.daggercoffee.MainActivity; import dagger.Component; /** * Created by wangjitao on 2016/11/3 0003. */ @Component(modules = SimpleModule.class) public interface SimpleComponent { void inject(MainActivity mainActivity) ; }
用@Component注解這個接口能讓Dagger2在需要注入時找到這個工具,同時還告訴Dagger2提供依賴的是SimpleModule這個類
當然,如果需要用這個Module給MainActivity注入我們的CoffeeMachine,還需要一個inject方法,里面傳入我們的MainActivity 對象的實例。
ok,最后再在我們的activity中使用以下
package com.qianmo.daggercoffee; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.Button; import android.widget.TextView; import com.qianmo.daggercoffee.bean.CoffeeMachine; import com.qianmo.daggercoffee.bean.SimpleComponent; import javax.inject.Inject; public class MainActivity extends AppCompatActivity implements View.OnClickListener { TextView tvCoffee; Button btnMake; @Inject CoffeeMachine coffeeMachine; private SimpleComponent simpleComponent; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); tvCoffee = (TextView) findViewById(R.id.tv_coffee); btnMake = (Button) findViewById(R.id.btn_make); btnMake.setOnClickListener(this); // Cooker cooker = new Cooker("wangjitao", "Es"); // coffeeMachine = new CoffeeMachine(cooker); simpleComponent = DaggerSimpleComponent.builder().simpleModule(getModule()).build(); simpleComponent.inject(this); } private String makeCoffee() { return coffeeMachine.makeCoffee(); } @Override public void onClick(View view) { tvCoffee.setText(makeCoffee()); } }
ok,這樣我們就簡單的使用了Dagger2·····