资讯专栏INFORMATION COLUMN

Dagger2入门教程

Juven / 2915人阅读

摘要:由此来看,解除了和的依赖,而和产生了依赖容器。依赖注入的好处解耦,解除对象之间的依赖关系。在出来之前,也存在一些依赖注入框架如。

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的依赖。

(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之间存在如下关系。

(3)其他更多工程上使用的问题可以参见ppt:dagger2源码分析

参考文献

android apt技术:https://blog.csdn.net/Ru_Zhan...

依赖注入:https://mp.csdn.net/mdeditor

案例地址:dagger案例源码

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/14340.html

相关文章

  • 听说你还不会用Dagger2Dagger2 For Android最佳实践教程

    摘要:的入门门槛其实是比较高的,据了解,目前有很多工程师对还不甚了解,没有用上或者是用法有问题,本文的主旨就是让工程师快速掌握并且优雅简洁地使用。这里为大家奉上一份在上的最佳实践教程。 showImg(https://segmentfault.com/img/remote/1460000016755870?w=960&h=600); 本文首发于我的个人博客 点击进入原文链接 前言 Dagge...

    nemo 评论0 收藏0
  • 听说你还不会用Dagger2Dagger2 For Android最佳实践教程

    摘要:的入门门槛其实是比较高的,据了解,目前有很多工程师对还不甚了解,没有用上或者是用法有问题,本文的主旨就是让工程师快速掌握并且优雅简洁地使用。这里为大家奉上一份在上的最佳实践教程。 showImg(https://segmentfault.com/img/remote/1460000016755870?w=960&h=600); 本文首发于我的个人博客 点击进入原文链接 前言 Dagge...

    陆斌 评论0 收藏0
  • [Android] Dagger2 入门 2

    摘要:的作用是提供对象的。由生成时会有类似依赖注入的操作。通过进行依赖注入。当需要对象时,直接从外部注入的取出即可。也就是说,在的生命周期内,返回的都是同一个对象。也就是说,如果要实现全局范围内的,必需要有全局的。 上一篇文章介绍了Dagger2的基本用法,这篇文章主要说一下Dagger2中@Scope的用法和原理。 上一篇文章中提到: 如上面例子所示,如果要求D对象为单例,可以通过@Si...

    Jrain 评论0 收藏0
  • [Android] Dagger2 入门 1

    摘要:是依赖图和被注入对象之间的桥梁。通常于标签同时使用。注意的方法必需至少符合以上两条规则中的一条。引入类和修改,如下如上即可实现对象的注入。为方便我们达到这一个目的,在中引入了这个功能。 这篇文章主要谈一下本人在学习Dagger2的心得,如有错漏,敬请谅解。 什么是依赖注入 依赖注入就是把下面这样的代码: class A { public A() { } class ...

    Nosee 评论0 收藏0
  • Java相关

    摘要:本文是作者自己对中线程的状态线程间协作相关使用的理解与总结,不对之处,望指出,共勉。当中的的数目而不是已占用的位置数大于集合番一文通版集合番一文通版垃圾回收机制讲得很透彻,深入浅出。 一小时搞明白自定义注解 Annotation(注解)就是 Java 提供了一种元程序中的元素关联任何信息和着任何元数据(metadata)的途径和方法。Annotion(注解) 是一个接口,程序可以通过...

    wangtdgoodluck 评论0 收藏0

发表评论

0条评论

Juven

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<