Dagger2是一個實現注入的框架,相信大家都聽說過ButterKnife,Dagger2和ButterKnife的作用是一樣的,但是實現的功能更加強大。ButterKnife只能注入View和事件,而Dagger2可以注入任何一個對象。本節主要介紹一下Dagger2的實用及實現的原理。
一、Dagger2的使用
首先我先用張圖來簡單說明一下Dagger2使用的流程
圖中的Module是應用中常用的功能模塊,比如說網絡訪問,數據存儲等。這個類會有一個@Module的註解,具體的代碼我會在後面詳細介紹。其實這個Module的作用是提供各種功能對象,而這些Module會放到一個有@Component的容器類中,也就是圖中Component類。當我們想使用各種功能對象進行業務操作的時候,只需要這個容器就能得到被註冊了的功能對象,圖中的意思是在Activity中使用對象進行業務操作,當然也不僅限於Activity。
上圖相對來說還是太簡略了,並沒有完整的表達出Dagger2的原理,下面我們直接從代碼中感受一個Dagger2的強大。
1.引入Dagger2
implementation 'com.google.dagger:dagger:2.4'
annotationProcessor 'com.google.dagger:dagger-compiler:2.4'
2.創建網絡訪問對象及其Module模塊
首先創建一個HttpObject,我們假設這個HttpObject中有各種網絡的操作,get,post,put等
public void get(){
Log.i("Dagger2","這裏是get方法");
}
public void post(){
Log.i("Dagger2","這裏是post方法");
}
}
創建HttpModule
@Module
public class HttpModule {
@Provides
public HttpObject providerHttpObject(){
return new HttpObject();
}
}
HttpModule的兩個註解是需要注意的地方:@Module這個註解相當於給當前類打了一個標記,表明了這個類的類型,便於注入到容器中;@Provides這個註解放在了方法的上面,從上面的代碼可以看出來,主要就是創建功能對象。
3.創建容器Component
@Component(modules = {HttpModule.class})
public interface MyComponent {
void injectMainActivity(MainActivity mainActivity);
}
容器這個類被@Component所註解,而且是一個接口類,@Component中的modules參數接收的類型是一個數組,表示被裝入容器的Module有哪些。injectMainActivity方法表示這個容器中的功能對象(例如HttpObject)會在哪個類使用,我這裏使用的MainActivity做的測試,所以參數寫的是MainActivity。
4.在類中使用HttpObject
在配置完上述的代碼之後,一定先rebuild!
public class MainActivity extends AppCompatActivity {
@Inject
HttpObject mHttpObject;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerMyComponent.create().injectMainActivity(this);
mHttpObject.get();
mHttpObject.post();
}
}
因爲我們要使用的類是HttpObject,所以在MainActivity創建這個類的對象,然後被@Inject所註解。要注意的是DaggerMyComponent這個類是rebuild之後生成的,調用DaggerMyComponent.create().inkectMainActivity(this)這句話來生成mHttpObject對象,調用HttpObject中的get和post方法就會有相應的輸出。
Dagger的優點
有的小夥伴會問,你整這麼一大堆是爲了啥?要不然直接創建對象,要不然創建一個單例,多省事,可比Dagger2的這種方式方便多了。在中大型項目中,類似於HttpObject這種對象會被大量的應用,如果突然有一天這個類的初始化方法改變了,你豈不是要修改每一處嗎。即便是使用單例getInstance方法也避免不了這種問題,因爲如果在創建對象的時候需要在構造器中添加一個參數,每一處的getInstance也需要被修改。而Dagger2完美的避免了這種問題
二、源碼分析
下面我們從源碼中分析Dagger2的實現流程
上面的代碼我們使用了create方法來創建MyComponent,這次我們使用建造者的方式來創建,Dagger2中提供了這種方式,如下代碼
DaggerMyComponent
.builder()
.httpModule(new HttpModule())
.build()
.injectMainActivity(this);
其實我們從create的方法中進入就可以看到,其實create中包裝了建造者的方式。
builder方法其實沒什麼說的,點進去看,其實就是Builder的創建,代碼我就不貼出來了。
httpModule這個方法肯定就是相應Module的創建了,代碼如下,我們可以看出來,源代碼中只是做了一個對象非空的檢查
public Builder httpModule(HttpModule httpModule) {
this.httpModule = Preconditions.checkNotNull(httpModule);
return this;
}
而build方法主要就DaggerMyComponent的創建,代碼如下:
public MyComponent build() {
if (httpModule == null) {
this.httpModule = new HttpModule();
}
return new DaggerMyComponent(this);
}
以上的代碼都沒什麼特別的,主要就是建造者模式的只用,下面我們來分析一下injectMainActivity方法的邏輯。
我們點擊進入injectMainActivity這個方法,會進入到我們所寫的MyComponent這個接口方法中,點擊圖中紅框的位置會出現接口的實現類或者直接進入到實現類
MyComponent的實現類其實我們已經見到了,就是DaggerMyComponent,其中injectMainActivity的實現方法如下:
@Override
public void injectMainActivity(MainActivity mainActivity) {
mainActivityMembersInjector.injectMembers(mainActivity);
}
mainActivityMembersInjector的類是MembersInjector<T>這個接口類,同樣的我們查看這個方法的實現類方法:
我們發現實現類有兩個,那我們查看哪個呢?我們來這樣看:首先將項目切換到project模式,依次進入如下目錄:app->build->generated->source->apt->debug,到這裏,我們就發現了我們項目的包,可以看見生成了幾個類,如圖:
而圖中紅框標註的正是MembersInjector<T>的實現類,我們想要找的就是它,我們來查看injectMembers方法的實現:
private final Provider<HttpObject> mHttpObjectProvider;
@Override
public void injectMembers(MainActivity instance) {
if (instance == null) {
throw new NullPointerException("Cannot inject members into a null reference");
}
instance.mHttpObject = mHttpObjectProvider.get();
}
代碼中的instance是MainActivity,而mHttpObject是MainActivity中的mHttpObject(從這裏我們可以看出來,想要被註解的對象修飾符一定是public),mHttpObject是通過mHttpObjectProvider.get()獲得的,mHttpObjectProvider的類是Provider,而Provider是一個接口,我們如果還是使用上面的方法來找實現類,你會發現實現類有四五十個,那怎麼辦呢?
從上面系統幫我們生成的類中有如下這麼一個類,而這個類這是我們想要找的Provider的實現類,我們點擊去查看get方法的實現:
@Override
public HttpObject get() {
return Preconditions.checkNotNull(
module.providerHttpObject(), "Cannot return null from a non-@Nullable @Provides method");
}
get方法所返回的是module通過providerHttpObject方法創建的,這個module是HttpModule,點擊去查看providerHttpObject:
@Module
public class HttpModule {
@Provides
public HttpObject providerHttpObject(){
return new HttpObject();
}
}
這個不就是我們的HttpModule中的方法嗎?是的,Dagger框架繞了一大圈創建了HttpObject這個對象。
以上就是Dagger創建對象的流程