1、Android依賴注入簡介
1.1 依賴注入(IOC:Inversion of Control)
(1)依賴注入概念
依賴注入將來單說就是非自己主動初始化依賴,而通過外部來傳入依賴的方式,我們就稱爲依賴注。舉例來說:如下面的代碼所示,A是依賴注入的例子,B是非依賴注入的例子。汽車(Car)依賴輪胎(Tyre)的資源。
- 如果在Car內部自己去new一個Tyre的資源(如B所示),那麼Car就和Tyre產生了前的依賴,既Car->Tyre。如果Tyre的型號發生了改變,那麼Car的代碼就需要跟隨發生改變。
- 但是如果Car不直接new一個Tyre,而是依賴於外部傳入的Tyre,那麼Car就解除了Tyre的依賴,不管外部的Tyre怎麼改變,Car的代碼永遠不會發生改變。
【A:依賴注入的例子】
public class Car {
...
// 輪胎
Tyre tyre;
...
public Car(Tyre tyre) {
this.tyre = tyre;
}
}
【B:非依賴注入的例子】
public class Car{
...
// 輪胎
Tyre tyre;
...
public Car(Tyre tyre) {
this.tyre = new Tyre();
}
}
(2)Ioc容器:負責依賴注入控制器
如A所示,上文已經說了Car的實現依賴於外界傳入的Tyre。那麼負責傳入這個資源的角色就是Ioc容器。Ioc容器本身也不實際產生資源,它會請求其他的對象來生成資源。由此來看,Car解除了和Tyre的依賴,而和Ioc產生了依賴:Car->Ioc容器。這樣的好處是,如果Car依賴於多個對象(方向盤,輪胎,地盤等等),那麼Car不必要和那麼多對象產生直接的依賴,而只需要依賴ioc容器即可。不管Car需要什麼資源,它只需要找ioc容器要即可。由此可以看出,依賴注入就是要解決對象之間的依賴關係,即由對象->對象的直接依賴,轉化到對象->ioc的依賴。
(3)依賴注入的好處
- 解耦,解除對象之間的依賴關係。
- 因爲已經解耦,所以方便做單元測試,尤其是 Mock 測試
- 其他
1.2 Android依賴注入
在Java裏面,存在大量的依賴注入的框架,如Spring等等。在Dagger2出來之前,Android也存在一些依賴注入框架如Dagger1。其均是依靠反射來實現依賴注入,因此會帶來一定的性能問題,因此這些框架並沒有在Android中流行起來。
Dagger2通過APT技術,通過在編譯的時候生成注入的工廠類代碼,解決了性能問題,因此Dagger2開始在Android上發揚光大。
2、dagger2原理
2.1 dagger2原理簡析
Dagger2實現原理如C圖所示,其主要由三部分組成
- 被注入對象:有屬性需要被設置
- module:對象的生成器
- component:對象屬性的設置器(對應上面的Ioc控制器)
【C:dagger2實現原理】
總結起來就是,當被注入的對象需要Component去給他設置屬性的時候,Component就會去找它的Module去生成該對象。如果Module完成不了這件事情,Component就會去找它依賴的Component(Dependency Component)去給他生成該對象。Dependency Component本身也沒有生成對象能力,其就依賴它的Module去生成該對象
2.2 dagger2常用註解
在瞭解Dagger2註解之前我們首先記住2個東西:
- Dagger2所有對象匹配都是按照返回類型來匹配,與函數命名無關
- Dagger2的Scope的大小都是我們人爲賦予的,Scope的大小不是名字決定的,而是Component之間的依賴關係決定的。
在2.1中所提的三種角色都是通過註解來完成的,dagger2常用註解如下:
(1)Component使用的註解
- @Component
- 作用對象:Class
- 表明該class是個Component
- @Scope
- 作用對象:Class
- 指明Component 範圍,其依賴的Model如果用@Scope修飾,必須與Component 的Scope相同
Component示例如下所示:
- 因爲ActivityComponent用@Component修飾了,因此示一個Component,其可用來注入一個對象(給對象屬性賦值)
- 其依包含的Module是ActivityModule.class,並且可以包含Module
- 其依賴於AppComponent.class,也可以依賴多個Component
@BigScoped
@Component(modules = {ActivityModule.class},dependencies = {AppComponent.class})
public interface ActivityComponent {
void inject(Dagger2Activity dagger2Activity);
ActivityScopeObj activityScopeObj();
ActivityObj activityObj();
}
(2)Module使用的註解
- @Module
- 作用對象:Class
- 表明該Class是個對象的生成器
- @Provider
- 作用對象:Method
- 表明該方法能對外提供對象:按照返回的類型匹配
- @Scope
- 作用對象:Method
- 表明該方法在Scope範圍內是個單例
Module示例:
- 其用@Module修飾,表明其示一個Module,屬於某一個Component
- 只有用@Provides修飾的函數纔是Module能提供的對象,其按照返回的類型來進行匹配,與函數命名無關
- @BigScoped:表示一個ActivityComponent的實例只會返回一個ActivityScopeObj的對象。原理可參見:
@Module
public class ActivityModule {
@Provides
public ActivityObj provideActivityObj() {
return new ActivityObj();
}
@Provides
public FragmentNotVisibleObj provideActivityNotVisibleObj() {
return new FragmentNotVisibleObj();
}
@BigScoped
@Provides
public ActivityScopeObj provideActivityScopeObj() {
return new ActivityScopeObj();
}
}
(3)被注入對象使用的註解
- @Inject
- 作用對象:Field
- 表明該屬性依賴Component進行注入
代碼示例:
public class Dagger2Activity extends AppCompatActivity implements ActivityDataProvider {
@Inject
ActivityObj mActivityObj;
@Inject
FragmentNotVisibleObj mActivityNotVisibleObj;
@Inject
ActivityScopeObj mActivityScopeObj;
ActivityComponent component = DaggerActivityComponent.builder()
.activityModule(new ActivityModule())
.build();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_dagger2_test);
// 使用component來給Dagger2Activity的屬性賦值
component.inject(this);
再看看ActivityComponent生命的inject方法
- inject方法示Component留給被注入對象將自己傳進來的入口,這個名字可隨便命名
- 其參數必須對應一個具體的對象,不能是基類,也不能是父類
public interface ActivityComponent {
void inject(Dagger2Activity dagger2Activity);
3、dagger2案例
案例地址:dagger案例源碼
3.1 申明Scope
// BigScope
@Documented
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface BigScoped {
}
// SmallScope
@Documented
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface SmallScoped {
}
3.2 Dagger2Activity的注入
(1)Dagger2Activity
- 其依賴於ActivityComponent來給他注入三個屬性mActivityObj,mActivityNotVisibleObj和mActivityScopeObj
- 其中mActivityScopeObj在Module裏面用Scope修飾了,因此一個ActivityComponent的實例只會返回一個mActivityScopeObj對象
public class Dagger2Activity extends AppCompatActivity implements ActivityDataProvider {
@Inject
ActivityObj mActivityObj;
@Inject
FragmentNotVisibleObj mActivityNotVisibleObj;
@Inject
ActivityScopeObj mActivityScopeObj;
ActivityComponent component = DaggerActivityComponent.builder()
.activityModule(new ActivityModule())
.build();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_dagger2_test);
// 使用component來給Dagger2Activity的屬性賦值
component.inject(this);
Dagger2Fragment dagger2Fragment = new Dagger2Fragment();
dagger2Fragment.setDataProvider(this);
getFragmentManager().beginTransaction()
.addToBackStack(null)
.replace(R.id.container, dagger2Fragment)
.commit();
}
@Override
public ActivityComponent getComponent() {
return component;
}
@Override
public ActivityScopeObj getActivityScopeObj() {
return mActivityScopeObj;
}
}
(2)ActivityComponent
- 使用ActivityModule來生成對象
- 同時依賴AppComponent來生成對象
- 其他參見注釋:註釋很重要
@BigScoped
@Component(modules = {ActivityModule.class},dependencies = {AppComponent.class})
public interface ActivityComponent {
// 暴露接口給Dagger2Activity,讓其傳入自己,好給Dagger2Activity屬性賦值
void inject(Dagger2Activity dagger2Activity);
// 暴露給其他Component的接口,只有暴露的接口,其他的Component纔可以依賴於它創建對象,按照返回類型匹配,與函數名無關
ActivityScopeObj activityScopeObj();
ActivityObj activityObj();
}
(3)ActivityModule
- ActivityModule可以提供三種對象ActivityObj,FragmentNotVisibleObj,ActivityScopeObj
- ActivityScopeObj上文已經解釋了
- 由於FragmentNotVisibleObj沒有在ActivityComponent中暴露,因此FragmentComponent(參見3.2-2和3.3)不能依賴其提供FragmentNotVisibleObj類型的對象
@Module
public class ActivityModule {
@Provides
public ActivityObj provideActivityObj() {
return new ActivityObj();
}
@Provides
public FragmentNotVisibleObj provideActivityNotVisibleObj() {
return new FragmentNotVisibleObj();
}
@BigScoped
@Provides
public ActivityScopeObj provideActivityScopeObj() {
return new ActivityScopeObj();
}
}
【Dagger2Activity的整體注入過程如下圖】
從圖中可以看出dagger2的實現模型和上面介紹的Ioc控制反轉模型表現一致
3.2 Dagger2Fragment的注入
(1)Dagger2Fragment
- 需要注入三個對象
- mActivityObj由ActivityComponent生成
- mActivityScopeObj由ActivityComponent生成
- mFragmentObj有FragmentComponent生成
- 其他參見注釋
public class Dagger2Fragment extends android.app.Fragment {
ActivityDataProvider mProvider;
@Inject
ActivityObj mActivityObj;
@Inject
ActivityScopeObj mActivityScopeObj;
@Inject
FragmentObj mFragmentObj;
@Override
public View onCreateView(LayoutInflater inflater, @Nullable final ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.dagger2_fragment, container, false);
FragmentComponent component = DaggerFragmentComponent.builder()
.fragmentModule(new FragmentModule())
// 依賴於ActivityComponent,是由Activity傳進來的,因此次數使用的activityComponent和Dagger2Activity使用的Component是同一個對象
// 因此 mActivityScopeObj和mProvider.getActivityScopeObj()得到的是同一個對象
.activityComponent(mProvider.getComponent())
.build();
component.inject(this);
// true
Logger.d("ActivityScope single instance:" + (mActivityScopeObj == mProvider.getActivityScopeObj()));
// false
Logger.d("not Scope Object:" + (mActivityObj == mFragmentObj.mActivityObj));
return view;
}
public void setDataProvider(ActivityDataProvider provider) {
mProvider = provider;
}
}
(2)FragmentComponent
@Component(modules = {FragmentModule.class},dependencies = {ActivityComponent.class})
@SmallScoped
public interface FragmentComponent {
void inject(Dagger2Fragment dagger2Fragment);
}
(3)FragmentObj
public class FragmentObj {
@Inject
ActivityObj mActivityObj;
// 用@Inject修飾構造和在Module用@Provide修飾等價
@Inject
public FragmentObj() {
}
}
4、dagger2入門參見疑惑問題
(1)查找對象按照什麼匹配?
按照類型匹配
- 兩個函數是完全等價的
@Module
public class ActivityModule {
@Provides
public ActivityObj provideActivityObj() {
return new ActivityObj();
}
@Provides
public ActivityObj ActivityObj() {
return new ActivityObj();
}
(2)Scope之間的依賴關係怎麼確定?
按照其修飾的Component間的依賴關係決定,還是上面的例子:
@Component(modules = {FragmentModule.class},dependencies = {ActivityComponent.class})
@SmallScoped
public interface FragmentComponent {
void inject(Dagger2Fragment dagger2Fragment);
}
@BigScoped
@Component(modules = {ActivityModule.class},dependencies = {AppComponent.class})
public interface ActivityComponent {
void inject(Dagger2Activity dagger2Activity);
ActivityScopeObj activityScopeObj();
ActivityObj activityObj();
}
因爲FragmentComponent–>ActivityComponent。因此,可以得出ActivityComponent實例生命週期長於FragmentComponent的實例(不然,FragmentComponent要傳入AppComponent依賴的時候,沒有可以賦的值),因此可以得出SmallScope和BiggerScope之間存在如下關係。
Scope實現原理可以參見:dagger2源碼分析
(3)其他更多工程上使用的問題可以參見ppt:dagger2源碼分析