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个东西:

  1. Dagger2所有对象匹配都是按照返回类型来匹配,与函数命名无关
  2. 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控制反转模型表现一致
Dagger2入门教程

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源码分析

参考文献

相关推荐