Github地址git
Dagger是一個快速的依賴注入框架,供Android和Java開發使用。之前由Square維護,如今轉交給Google,Github連接爲:github.com/google/dagg…github
Dagger爲依賴注入而生。什麼是依賴?什麼是注入?爲何要使用依賴注入?這是咱們學習Dagger以前必須瞭解的。
依賴就是一個類中要使用其餘的類來完成某些工做,這樣一個類就依賴了另一個類。好比在MainActivity中必須使用一個User類的對象,那麼MainActivity就依賴了User類。bash
public class MainActivity extends AppCompatActivity {
User mUser;//MainActivity依賴User類
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mUser = new User();//初始化mUser
setContentView(R.layout.activity_main);
}
}複製代碼
通常咱們直接在MainActivity中直接初始化mUser對象,但若是有天User類的構造方法中增長了一個參數,好比用戶名,那麼全部調用User的無參構造方法的地方所有要修改,這顯然不是咱們但願看到的。因而咱們思考可不能夠提供一個User的工廠類或者容器類專門負責User對象的建立,這樣User對象的建立就不會跟MainActivity發生耦合,無論User的構造方法如何變,都不會影響到MainActivity。那麼代碼多是這樣:app
public class MainActivity extends AppCompatActivity {
User mUser;//MainActivity依賴User類
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mUser = UserFactory.getUser()//經過工廠類注入mUser
setContentView(R.layout.activity_main);
}
}複製代碼
那麼mUser對象就不是在MainActivity建立,而是由UserFactory建立,設置給mUser對象,這就能夠稱之爲mUser被注入了,這樣MainActivity就不須要關心User對象是怎樣建立出來,對User對象建立的修改就不會影響到MainActivity裏面的代碼,這就是依賴注入的好處的。這就比如咱們打針,人體依賴藥液來治療疾病,但藥液並非人體本身生產的,而是醫藥公司生產而後經過注射器注入到人體,而咱們人體根本就無需關心藥液是如何被製造出來的。框架
Dagger的做用就是經過註解的方式,幫咱們自動生成建立對象的工廠類。ide
可經過連接github.com/google/dagg…查找最新版本學習
dependencies {
compile 'com.google.dagger:dagger:2.11'
annotationProcessor 'com.google.dagger:dagger-compiler:2.11'
}複製代碼
分別在MainActivity的User成員變量和User的構造方法上添加@Inject依賴。ui
public class MainActivity extends AppCompatActivity {
//成員變量上添加註解
@Inject
User mUser;
}
public class User {
String name;
//構造方法上添加註解
@Inject
public User() {
this.name = "Leon";
}
}複製代碼
事實上完成第二步後,MainActivity中成員mUser並無調用構造方法完成初始化,還須要一個類來完成注入,這就是Component類。
咱們建立一個MainComponent接口,提供一個inject方法,其參數爲將被注入的類MainActivitythis
public interface MainComponent {
void inject(MainActivity activity);
}複製代碼
而後,咱們在Android Studio下選擇菜單build->Make Project,這時候,會在app模塊下build/generated/source/apt/debug/包名/目錄下生成三個文件DaggerMainComponent,MainActivity_MembersInjector,User_Factory。DaggerMainComponent即爲接口MainComponent的實現類。google
最後,咱們可使用生成的DaggerMainComponnet完成注入,成員變量mUser將被賦值。若是是老司機,是否是會以爲這跟ButterKnife.bind(this)有殊途同歸之妙呢?
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//注入,內部會調用User的構造方法完成成員變量mUser的初始化
DaggerMainComponent.builder().build().inject(this);
//打印結果,mUser不爲null
Log.d(TAG, "onCreate: " + mUser.name);
}複製代碼
假如我想在一個Activity裏面注入一個TextView,以下:
public class MainActivity extends AppCompatActivity {
@Inject
TextView mTextView;
}複製代碼
若是按照上述步驟,就須要找到TextView的構造方法,加上一個@Inject註解,這個顯然是沒法作到的。Module就是爲了解決不可以經過註解構造方法來建立一個實例的問題。咱們建立一個TextViewMoudle類,使用@Module註解。另外須要提供一個方法返回一個TextView實例,而且用@Provides註解。
@Module
public class TextViewModule {
@Provides
TextView provideTextView(Context context) {
return new TextView(context);
}
}複製代碼
provideTextView方法有個Context參數,須要外界傳入到TextViewModule,這裏給TextViewModule提供一個帶有Context參數的構造方法。另外還須要提供個provideContext方法返回上下文,由於Dagger框架調用provideTextView方法獲取一個TextView實例時,發現要傳一個Context類型的參數,這時候他會查找被@Provides註解而且返回值爲Context類型的方法獲取一個Context實例傳入provideTextView方法。
@Module
public class TextViewModule {
//保存一個上下文成員變量
private Context mContext;
//構造方法接收一個上下文
public TextViewModule(Context context) {
this.mContext = context;
}
@Provides
TextView provideTextView(Context context) {
return new TextView(context);
}
@Provides
Context provideContext() {
return mContext;
}
}複製代碼
接下來還須要在MainComponent接口上指定TextViewModule。而後點擊菜單選項build->Make Project,這時又會在build/generated/source/apt/debug/包名/目錄下生成兩個新文件TextViewModule_ProvideContextFactory和TextViewModule_ProvideTextViewFactory。
@Component(modules = TextViewModule.class)
public interface MainComponent {
void inject(MainActivity activity);
}複製代碼
最後在注入時建立一個TextViewModule傳入,表示告訴Dagger框架能夠從TextViewModule中獲取TextView實例。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//傳入TextViewMoudle
DaggerMainComponent.builder().textViewModule(new TextViewModule(this)).build().inject(this);
FrameLayout frameLayout = (FrameLayout) findViewById(R.id.frame);
//mTextView已被注入,不爲null
mTextView.setText(mUser.name);
frameLayout.addView(mTextView);
}複製代碼