最近公司的項目是用mvp+dagger2搭的框架,因爲以前沒接觸過dagger2,改bug和作需求老是一臉懵逼,看了些文檔介紹,和大多數學習者同樣從Dependency Injection、註解概念等等開始瞭解,而後敲代碼上手,在此記錄下學習心得。既然是入門,那些概念和註解的歷史就不介紹了,Google一下你就知道,直接介紹最最基本的使用以及具體實現原理。html
先看一個例子,MainActivity依賴Province,Province依賴City,City依賴Street;java
Street.javaandroid
public class Street {
public Street(){}
public String show(){
return "人民南路";
}
}複製代碼
City.javagit
public class City {
public Street street;
public City(Street street) {
this.street = street;
}
public String show() {
return "成都市" + street.show();
}
}複製代碼
Province.javagithub
public class Province {
public City city;
public Province(City city) {
this.city = city;
}
public String showAddress() {
return "四川省" + city.show();
}
}複製代碼
MainActivity.javabash
public class MainActivity extends AppCompatActivity {
public Street street;
public City city;
public Province province;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
street = new Street();
city = new City(street);
province = new Province(city);
Log.d("hcy", "onCreate: " + province.showAddress());
}
}複製代碼
能夠看到,爲了獲取地址信息,在代碼中須要實例化各類依賴到的對象,一旦依賴過多就容易影響代碼閱讀,那麼配合使用@Inject和@Component又是怎樣的呢?框架
1.首先在build.gradle中添加依賴ide
dependencies {
compile 'com.google.dagger:dagger-android:2.11'
compile 'com.google.dagger:dagger-android-support:2.11' // if you use the support libraries
annotationProcessor 'com.google.dagger:dagger-android-processor:2.11'
annotationProcessor 'com.google.dagger:dagger-compiler:2.11'
}複製代碼
Street.java函數
public class Street {
@Inject
public Street(){}
public String show(){
return "人民南路";
}
}複製代碼
2.須要依賴的成員和提供依賴的成員構造函數用@Inject標註源碼分析
City.java
public class City {
@Inject
public Street street;
@Inject
public City(Street street) {
this.street = street;
}
public String show() {
return "成都市" + street.show();
}
}複製代碼
Province.java
public class Province {
@Inject
public City city;
@Inject
public Province(City city) {
this.city = city;
}
public String showAddress() {
return "四川省" + city.show();
}
}複製代碼
3.須要額外加一個接口MainActivityComponent.java,用@Component標註
@Component
public interface MainActivityComponent {
void inject(MainActivity activity);
}複製代碼
4.執行DaggerMainActivityComponent.create().inject(this)
此時的MainActivity.java
public class MainActivity extends AppCompatActivity {
@Inject
public Province province;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//in Android Studio, select Build > Rebuild Project
DaggerMainActivityComponent.create().inject(this);
Log.d("hcy", "onCreate: " + province.showAddress());
}
}複製代碼
先後的打印是一致的,能夠看到MainActivity的中本來須要實例化對象的那些代碼如今能夠省略了,有助於咱們更好地關注業務實現。
回顧下使用註解的步驟:
1.build.gradle中添加dagger2依賴
2.使用@Inject標註在構造函數和被引用的成員變量上
3.新建MainActivityComponent接口,並用@Component標註
4.在MainActivity中執行DaggerMainActivityComponent.create().inject(this);(第一次需Rebuild Project)
咱們在MainaActivity中加了DaggerMainActivityComponent.create().inject(this)這句代碼替換了以前的一些實例化的操做,那麼這句代碼具體作了哪些工做?原來Dagger2會在編譯過程當中生成對應的依賴項,這些依賴項在Android Studio該路徑下,如圖所示:
DaggerMainActivityComponent.create()
public static MainActivityComponent create() {
return builder().build();
}
public static Builder builder() {
return new Builder();
}
public static final class Builder {
private Builder() {
}
public MainActivityComponent build() {
return new DaggerMainActivityComponent(this);
}
}複製代碼
能夠看到,不論是經過builder().build()仍是create(),最後都會調用DaggerMainActivityComponent構造函數;在經過源碼閱讀後,能夠將整個過程分爲兩步,分別是initialize()和inject()
private DaggerMainActivityComponent(Builder builder) {
assert builder != null;
initialize(builder);
}
private void initialize(final Builder builder) {
this.cityMembersInjector = City_MembersInjector.create(Street_Factory.create());//註釋1
this.cityProvider = City_Factory.create(cityMembersInjector, Street_Factory.create());//註釋2
this.provinceMembersInjector = Province_MembersInjector.create(cityProvider);//註釋3
this.provinceProvider = Province_Factory.create(provinceMembersInjector, cityProvider);//註釋4
this.mainActivityMembersInjector = MainActivity_MembersInjector.create(provinceProvider);//註釋5
}複製代碼
在initialize()方法中對成員賦值,這裏的成員分爲兩類,分別以_MembersInjector和_Factory爲後綴;Xx_MembersInjector能夠理解爲注入器,用來實現Xx與內部成員的依賴:
public interface MembersInjector<T> {
void injectMembers(T instance);
}複製代碼
Xx_Factory是建立Xx的工廠:
public interface Provider<T> {
T get();
}
public interface Factory<T> extends Provider<T> {
}複製代碼
這裏先記住二者的功能,具體後面會分析
先看下Street_Factory.java
public final class Street_Factory implements Factory<Street> {
private static final Street_Factory INSTANCE = new Street_Factory();
@Override
public Street get() {
return new Street();
}
public static Factory<Street> create() {
return INSTANCE;
}
}複製代碼
代碼很簡單,經過惡漢式建立了一個Street_Factory單例(這裏的源碼可能會有不一樣,以前看過一版是經過枚舉建立的單例);再看下City_MembersInjector.java
public final class City_MembersInjector implements MembersInjector<City> {
private final Provider<Street> streetProvider;
public City_MembersInjector(Provider<Street> streetProvider) {
assert streetProvider != null;
this.streetProvider = streetProvider;
}
public static MembersInjector<City> create(Provider<Street> streetProvider) {
return new City_MembersInjector(streetProvider);
}
@Override
public void injectMembers(City instance) {
if (instance == null) {
throw new NullPointerException("Cannot inject members into a null reference");
}
instance.street = streetProvider.get();
}
}複製代碼
City_MembersInjector只是將傳進來的Street_Factory賦值給本身的成員變量;
後面的功能都同樣,就用僞代碼列出:
City_Factory.java
public final class City_Factory implements Factory<City> {
public City_Factory(...省略參數){
this.city_MembersInjector = city_MembersInjector;
this.Street_Factory = street_Factory;
}
}複製代碼
Province_MembersInjector.java
public final class Province_MembersInjector implements MembersInjector<Province> {
public Province_MembersInjector(...省略參數){
this.city_Factory = city_Factory
}
}複製代碼
Province_Factory.java
public final class Province_Factory implements Factory<Province> {
public Province_Factory(...省略參數){
this.province_MembersInjector = province_MembersInjector
this.city_Factory = city_Factory
}
}複製代碼
public final class MainActivity_MembersInjector implements MembersInjector<MainActivity> {
private final Provider<Province> provinceProvider;
public MainActivity_MembersInjector(Provider<Province> provinceProvider) {
this.province_Factory = provinceProvider;
}
}複製代碼
到此目標工廠和注入器都已經建立完成,可是此時目標對象和依賴關係還沒產生。
inject()經過調用injectMembers()完成真正目標對象實例化以及依賴操做,代碼也沒多少,就把整個完整的過程涉及到的代碼貼出來:
//DaggerMainActivityComponent.java
public void inject(MainActivity activity) {
mainActivityMembersInjector.injectMembers(activity);
}
//MainActivity_MembersInjector.java
public void injectMembers(MainActivity instance) {
if (instance == null) {
throw new NullPointerException("Cannot inject members into a null reference");
}
instance.province = provinceProvider.get();
}
//Province_Factory.java
public Province get() {
return MembersInjectors.injectMembers(
provinceMembersInjector, new Province(cityProvider.get()));
}
//Province_MembersInjector.java
public void injectMembers(Province instance) {
if (instance == null) {
throw new NullPointerException("Cannot inject members into a null reference");
}
instance.city = cityProvider.get();
}
//City_Factory.java
public City get() {
return MembersInjectors.injectMembers(cityMembersInjector, new City(streetProvider.get()));
}
//City_MembersInjector.java
public void injectMembers(City instance) {
if (instance == null) {
throw new NullPointerException("Cannot inject members into a null reference");
}
instance.street = streetProvider.get();
}
//Street_Factory.java
public Street get() {
return new Street();
}複製代碼
能夠看到inject()和initialize()恰好是相反的過程,直到找到依賴的源頭完成源頭對象的實例化,即這裏的Street_Factory的get()方法的返回值;在Street實例化完成以後返回給City完成City實例化,City實例化完以後對本身的成員從新賦值了一遍,即產生依賴關係:
//City_MembersInjector.java
public void injectMembers(City instance) {
if (instance == null) {
throw new NullPointerException("Cannot inject members into a null reference");
}
instance.street = streetProvider.get();
}複製代碼
instance.street賦值的時候調用了streetProvider.get(),這是一個怎樣的過程呢,仍是畫個圖吧
step1的時候已經完成了Street和City的實例化,接着執行instance.street = streetProvider.get(),這句代碼即下面step2的過程:
紅色的就變成垃圾了,因此在這個過程當中Street被new了兩次;繼續分析建立Province以及創建依賴的過程:
能夠看到在整個過程當中Street其實new了4次,若是依賴更多的話(好比最外層再加個Country),Street在內存中被new的次數也會*2,不過過程當中這些對象最終都會變成垃圾被回收(總以爲這是額外的開銷,依賴多了豈不是編譯就慢了?),而不用dagger2只要new一次就能夠了(以下圖),可是二者最終都是一條依賴鏈
整個流程:
黑色的流程線是initialize()的過程,用來建立目標實例的工廠和注入器;紅色流程線是inject()/injectMembers()的過程,用來建立目標實例以及實現依賴。最後在回過頭來看下@inject和@component這兩個標註,能夠得出一些結論:
1.若一個類(Xx)的構造函數被@inject標註,則該類編譯時會產生Xx_Factory;
2.若一個類中的成員變量被@inject標註,則該類編譯時會產生Xx_MembersInjector;
3.@Component標註的接口(Xx)在編譯時生成DaggerXx,負責將上面兩個聯繫起來。
一個圖
學習這兩個註解,配合@Inject和@Component使用完成和上面同樣的功能
和上面的使用前代碼一致
1.建立一個Module.java,這裏取名BeanModule.java(想怎麼取名字均可以,好比周杰倫.java,可是最好別這麼幹,官方推薦以Module爲後綴),並用@Module標註,建立目標實例的方法用@Provides標註(官方推薦方法名以provide爲前綴)以下:
@Module
public class BeanModule {
@Provides
Street providerStreet() {
return new Street();
}
@Provides
City providerCity(Street street) {
return new City(street);
}
@Provides
Province providerProvince(City city) {
return new Province(city);
}
}複製代碼
2.修改上面的MainActivityComponent.java接口,@Component後面多了一個配置,以下:
@Component(modules = BeanModule.class)
public interface MainActivityComponent {
void inject(MainActivity activity);
}複製代碼
modules的做用至關於告訴Component,當你須要建立對象的時候就到我這裏來拿。MainActivity不變,運行後的效果和上面一毛同樣。
先看編譯過程生成的文件:
一樣從DaggerMainActivityComponent.create().inject(this),這句代碼開始分析
DaggerMainActivityComponent.create()
public static MainActivityComponent create() {
return new Builder().build();
}
public static final class Builder {
private BeanModule beanModule;
private Builder() {}
public MainActivityComponent build() {
if (beanModule == null) {
this.beanModule = new BeanModule();
}
return new DaggerMainActivityComponent(this);
}
public Builder beanModule(BeanModule beanModule) {
this.beanModule = Preconditions.checkNotNull(beanModule);
return this;
}
}複製代碼
不論是經過builder().build()仍是create(),最後都會調用DaggerMainActivityComponent構造函數;和上面對比這裏多了一步,那就是建立了BeanModule實例,仍是把這個過程分紅兩步:initialize()和inject()
private DaggerMainActivityComponent(Builder builder) {
assert builder != null;
initialize(builder);
}
private void initialize(final Builder builder) {
this.providerStreetProvider = BeanModule_ProviderStreetFactory.create(builder.beanModule);
this.providerCityProvider =
BeanModule_ProviderCityFactory.create(builder.beanModule, providerStreetProvider);
this.providerProvinceProvider =
BeanModule_ProviderProvinceFactory.create(builder.beanModule, providerCityProvider);
this.mainActivityMembersInjector =
MainActivity_MembersInjector.create(providerProvinceProvider);
}複製代碼
initialize()方法對本身的成員賦值,建立了目標對象工廠和注入器,在這裏的注入器只有一個MainActivity_MembersInjector;
//DaggerMainActivityComponent.java
public void inject(MainActivity activity) {
mainActivityMembersInjector.injectMembers(activity);
}
//MainActivity_MembersInjector.java
public void injectMembers(MainActivity instance) {
if (instance == null) {
throw new NullPointerException("Cannot inject members into a null reference");
}
instance.province = provinceProvider.get();
}
//...省略中間的部分get()
//BeanModule_ProviderStreetFactory.java
public Street get() {
return Preconditions.checkNotNull(
module.providerStreet(), "Cannot return null from a non-@Nullable @Provides method");
}複製代碼
這裏的inject()和injectMembers()的工做和上面是同樣的,調用BeaModule中的方法建立須要的實例,若該實例在建立時依賴其餘實例,則調用BeaModule中的對應方法先建立依賴實例,直到找到源頭,最大的區別就是在Xx_Factory中少了一步從新建立依賴的過程。用一張圖來表示建立的過程就是
能夠看到,相比於上面那種在過程當中new不少實例的作法,這種作法顯然更加高效。多說一點,在這些生成的XX_Factory中還多了一個靜態方法,如:
//BeanModule_ProviderStreetFactory.java
/** Proxies {@link BeanModule#providerStreet()}. */
public static Street proxyProviderStreet(BeanModule instance) {
return instance.providerStreet();
}
//BeanModule_ProviderProvinceFactory.java
/** Proxies {@link BeanModule#providerProvince(City)}. */
public static Province proxyProviderProvince(BeanModule instance, City city) {
return instance.providerProvince(city);
}複製代碼
所以咱們能夠在MainActivity直接經過類名調用建立實例。
用圖來表示@Module、@Providers、@Component、@Inject之間的聯繫:
既然用@Inject和@Component就可以完成的功能,爲啥我還要多寫那些Module類和provider方法,別跟我說是由於編譯過程當中比較費時?原來@Inject並非萬能的,官網的介紹:
But
@Inject
doesn’t work everywhere:
- Interfaces can’t be constructed.
- Third-party classes can’t be annotated.
- Configurable objects must be configured!
若是咱們要注入的對象是個接口,接口不能被實例化;或者是咱們要注入的對象是第三方庫,咱們無法把@Inject標註在三方庫的構造函數上,真是這樣的麼?就拿接口舉例:
改改上面的例子,添加一個IShow接口
public interface IShow {
String show();
}複製代碼
修改City.java
public class City implements IShow {
@Inject
public Street street;
@Inject
public City(Street street) {
this.street = street;
}
@Override
public String show() {
return "成都市" + street.show();
}複製代碼
修改Province.java,讓它接收一個接口
public class Province {
@Inject
public IShow city;
@Inject
public Province(IShow city) {
this.city = city;
}
public String showAddress() {
return "四川省" + city.show();
}
}複製代碼
MainActivity不變,跑起來果真GG了
而配合使用@Module和@Provides則不會出現這種狀況。問題又來了,若是依賴的提供方同時存在以@Inject標註和以@Module、@Providers標註,會找哪一個?回到代碼中,把兩種寫法都補充完整,要回答這個問題仍是隻有看源碼了,經過編譯能夠看到生成的文件
這裏把兩個方案都導進來了,隨時待命,再看下DaggerMainActivityComponent.java的initialize()方法:
private void initialize(final Builder builder) {
this.providerStreetProvider = BeanModule_ProviderStreetFactory.create(builder.beanModule);
this.providerCityProvider =
BeanModule_ProviderCityFactory.create(builder.beanModule, providerStreetProvider);
this.providerProvinceProvider =
BeanModule_ProviderProvinceFactory.create(builder.beanModule, providerCityProvider);
this.mainActivityMembersInjector =
MainActivity_MembersInjector.create(providerProvinceProvider);
}複製代碼
能夠發現和上面分析@Module、@Providers的源碼是同樣的,以後的流程固然也同樣。所以若是兩種同時存在,會選擇@Module、@Providers,而另外一種只是個「備胎」...
固然dagger2還有不少很強大的功能,待續