Dagger是一個快速的依賴注入框架,供Android和Java開發使用。以前由Square維護,現在轉交給Google,Github連結為:github.com/google/dagg…
依賴注入
Dagger為依賴注入而生。什麼是依賴?什麼是注入?為什麼要使用依賴注入?這是我們學習Dagger之前必須瞭解的。
依賴就是一個類中要使用其他的類來完成某些工作,這樣一個類就依賴了另外一個類。比如在MainActivity中必須使用一個User類的物件,那麼MainActivity就依賴了User類。
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。那麼程式碼可能是這樣:
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的作用就是通過註解的方式,幫我們自動生成建立物件的工廠類。
Dagger 2使用
1. 新增依賴
可通過連結github.com/google/dagg…查詢最新版本
dependencies {
compile 'com.google.dagger:dagger:2.11'
annotationProcessor 'com.google.dagger:dagger-compiler:2.11'
}複製程式碼
2. 新增註解@Inject
分別在MainActivity的User成員變數和User的構造方法上新增@Inject依賴。
public class MainActivity extends AppCompatActivity {
//成員變數上新增註解
@Inject
User mUser;
}
public class User {
String name;
//構造方法上新增註解
@Inject
public User() {
this.name = "Leon";
}
}複製程式碼
3. 建立Component類
事實上完成第二步後,MainActivity中成員mUser並沒有呼叫構造方法完成初始化,還需要一個類來完成注入,這就是Component類。
我們建立一個MainComponent介面,提供一個inject方法,其引數為將被注入的類MainActivity
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的實現類。
4. 注入
最後,我們可以使用生成的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);
}複製程式碼
Module使用
假如我想在一個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);
}複製程式碼