Dagger2入门教程
1、Android依赖注入简介
1.1 依赖注入(ICO: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不必要和那么多对象产生直接的依赖,而只需要依赖Ico容器即可。不管Car需要什么资源,它只需要找Ico容器要即可。由此可以看出,依赖注入就是要解决对象之间的依赖关系,即由对象->对象的直接依赖,转化到对象->Ico的依赖。
<img src="https://img-blog.csdn.net/201...; width="60%">
(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实现原理】
<img src="https://img-blog.csdn.net/201...; width="60%">
总结起来就是,当被注入的对象需要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之间存在如下关系。
<img src="https://img-blog.csdn.net/201...; width="70%">
(3)其他更多工程上使用的问题可以参见ppt:dagger2源码分析
参考文献
- android apt技术:https://blog.csdn.net/Ru_Zhan...
- 依赖注入:https://mp.csdn.net/mdeditor
- 案例地址:dagger案例源码