最近幾天在看一些新的Android技術,突然看到Dagger2這個,去github上面看了下1W+的star了,趕緊看下吧
1.簡介
Dagger-匕首,鼎鼎大名的Square公司旗下又一把利刃(沒錯!還有一把黃油刀,喚作ButterKnife)。
Dagger2起源於Dagger,是一款基於Java註解來實現的完全在編譯階段完成依賴注入的開源庫,主要用於模塊間解耦、提高代碼的健壯性和可維護性。Dagger2在編譯階段通過apt利用Java註解自動生成Java代碼,然後結合手寫的代碼來自動幫我們完成依賴注入的工作。
起初Square公司受到Guice的啓發而開發了Dagger,但是Dagger這種半靜態半運行時的框架還是有些性能問題(雖說依賴注入是完全靜態的,但是其有向無環圖(Directed Acyclic Graph)還是基於反射來生成的,這無論在大型的服務端應用還是在Android應用上都不是最優方案)。因此Google工程師Fork了Dagger項目,對它進行了改造。於是變演變出了今天我們要討論的Dagger2,所以說Dagger2其實就是高配版的Dagger。(轉自https://github.com/BaronZ88/Blog/blob/master/開源框架解析/Dagger2)
dagger2是一種依賴注入的編譯時框架
依賴注入理解
在一般的java程序中某個對象依賴其他對象可以通過成員變量直接引用,這種情況一般是主動傳入的比如常見的setData這種方法,而依賴注入則是說,不需要用戶手動設置,程序在編譯器期通過註解處理器,自動給相關對象添加依賴關係。
適用對象
Java 和Android (A fast dependency injector for Android and Java)
2.導入使用
Android studio3.0導入dagger2
compile 'com.google.dagger:dagger:2.7'
annotationProcessor 'com.google.dagger:dagger-compiler:2.7'
dagger2的庫可以直接在Androidstudio裏面看到,dagger-compiler的庫可以在下面的路徑中找到
Mac系統默認下載到:/Users/(用戶名)/.gradle/caches/modules-2/files-2.1
Windows系統默認下載到:C:\Users\(用戶名)\.gradle\caches\modules-2\files-2.1
3.基本語法介紹
Dagger2是怎麼通過註解來注入的呢?
首先想下平常寫註解就是@xxxx這樣就好了,那麼Dagger2用的都是那些註解:
@Inject:@Inject有兩個作用,一是用來標記需要注入的變量,以此告訴Dagger2爲它提供注入;第二個作用是標記構造函數,當構造函數被@Inject標記,dagger2爲@Inject標記的變量創建實例的時候就是通過這個註解找到合適的構造函數的
@Module:@Module用於封裝提供依賴的類。爲什麼需要專門提供這個註解,因爲在很多情況下,我們需要注入的類是第三方類庫,我們無法再構造函數直接加@Inject註解,還有一個問題就是如果有多個構造函數的問題,Module封裝依賴就是爲了解決這些問題。
@Provides:@Provides和Module配合使用,用來標註方法,用@Provide表明這個方法是對外提供注入對象的,提供的對象就是方法的返回類型。
@Component:@Component用於標註接口或者抽象類,是依賴需求方和依賴提供方之間的橋樑。被Component標註的接口在編譯時會生成該接口的實現類帶有Dagger前綴
4.實戰練習
public class Book {
@Inject
public Book() {
}
}
自己寫的一個對象所以可以直接在構造函數上加@Inject註解表明這個構造函數可以在需要的時候被使用生成實例
之前說過當需要注入依賴某個對象的時候直接在聲明上面直接加@Inject就行,因此只需要聲明實例的時候帶上就好了
最後想怎麼把這兩個類關聯起來呢,這個就是@Component
@Component
public interface BookComponent {
void inject(MainActivity activity);
}
寫完這個類以後build一下會發現生成了DaggerBookComponent
最後看下完整的調用是什麼
public class MainActivity extends AppCompatActivity {
@Inject
Book mBook;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerBookComponent.builder()
.build()
.inject(this);
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.e("wyd", "mBook: " + mBook.toString());
}
});
}
}
這時候可以運行一下可以看到這個實例已經可以正常使用了,這個時候可以看下DaggerBookComponent
生成的,打開這個文件可以看到
public final class DaggerBookComponent implements BookComponent {
private MembersInjector<MainActivity> mainActivityMembersInjector;
private DaggerBookComponent(Builder builder) {
assert builder != null;
initialize(builder);
}
public static Builder builder() {
return new Builder();
}
public static BookComponent create() {
return builder().build();
}
@SuppressWarnings("unchecked")
private void initialize(final Builder builder) {
this.mainActivityMembersInjector = MainActivity_MembersInjector.create(Book_Factory.create());
}
@Override
public void inject(MainActivity activity) {
mainActivityMembersInjector.injectMembers(activity);
}
public static final class Builder {
private Builder() {}
public BookComponent build() {
return new DaggerBookComponent(this);
}
}
}
public enum Book_Factory implements Factory<Book> {
INSTANCE;
@Override
public Book get() {
return new Book();
}
public static Factory<Book> create() {
return INSTANCE;
}
}
Book_Factory這個類也是通過註解來生成的很簡單,注意它是一個枚舉類型,所以直接返回INSTANCE,通過get方法返回Book對象,因爲這裏的自動生成都涉及到dagger-complier庫,這個庫看起來實現比較麻煩,裏面涉及到好多java解析的,暫時不深究,後邊有時間可以找個重點的瞭解下
這裏可以看到通過Builder生成DaggerBookComponent實例最終調用initialize生成mainActivityMembersInjector
這個變量顧名思義就是Mainactivity的成員注入器,MainAcitivity需要的注入都是由它提供,至於這個類的生成可以簡單想下就是Dagger的註解處理器通過解析@inject標籤,來獲得它需要注入的類,然後合起來生成MainActivity_MembersInjector
最終我們調用inject方法就直接調用 mainActivityMembersInjector.injectMembers(activity);看下這個方法
public final class MainActivity_MembersInjector implements MembersInjector<MainActivity> {
private final Provider<Book> mBookProvider;
public MainActivity_MembersInjector(Provider<Book> mBookProvider) {
assert mBookProvider != null;
this.mBookProvider = mBookProvider;
}
public static MembersInjector<MainActivity> create(Provider<Book> mBookProvider) {
return new MainActivity_MembersInjector(mBookProvider);
}
@Override
public void injectMembers(MainActivity instance) {
if (instance == null) {
throw new NullPointerException("Cannot inject members into a null reference");
}
instance.mBook = mBookProvider.get();
}
public static void injectMBook(MainActivity instance, Provider<Book> mBookProvider) {
instance.mBook = mBookProvider.get();
}
}
這裏就是完成了賦值instance.mBook = mBookProvider.get();
因此我們這裏就可以直接使用注入的類了這裏有個問題如果我們要注入多個類呢,加一個Phone的類,使用方法和Book一樣,看下Dagger重新生成的這個類
public final class MainActivity_MembersInjector implements MembersInjector<MainActivity> {
private final Provider<Book> mBookProvider;
private final Provider<Phone> mPhoneProvider;
public MainActivity_MembersInjector(
Provider<Book> mBookProvider, Provider<Phone> mPhoneProvider) {
assert mBookProvider != null;
this.mBookProvider = mBookProvider;
assert mPhoneProvider != null;
this.mPhoneProvider = mPhoneProvider;
}
public static MembersInjector<MainActivity> create(
Provider<Book> mBookProvider, Provider<Phone> mPhoneProvider) {
return new MainActivity_MembersInjector(mBookProvider, mPhoneProvider);
}
@Override
public void injectMembers(MainActivity instance) {
if (instance == null) {
throw new NullPointerException("Cannot inject members into a null reference");
}
instance.mBook = mBookProvider.get();
instance.mPhone = mPhoneProvider.get();
}
public static void injectMBook(MainActivity instance, Provider<Book> mBookProvider) {
instance.mBook = mBookProvider.get();
}
public static void injectMPhone(MainActivity instance, Provider<Phone> mPhoneProvider) {
instance.mPhone = mPhoneProvider.get();
}
}
大家可以看到這裏創建MainActivity_MembersInjector 傳入了兩個provider,賦值的時候也調用了各自的provider賦值
還有個問題如果我們使用的對象是個有參數的對象該怎麼調用呢?
直接聲明一個試下:
Error:(11, 12) 錯誤: Types may only contain one @Inject constructor.
直接報錯了,這裏我們就需要使用Module了,後邊有時間繼續