Dagger2入门

1.Dagger2简介

1.1 Dagger2的描述

Github地址:Dagger2

Dagger2官网上介绍是:A fast dependency injector for Android and Java.

Dagger2是提供给Android和Java使用的快速依赖注入框架,目的是为了解耦.

1.2 Dagger2的前世今生

Dagger2是由谷歌接手开发,最早版本的Dagger1 是由Square公司开发的.

相比于Dagger1,Dagger2有更好的性能,它使用的是预编译期间生成代码来完成依赖注入,而不像Dagger1那样通过反射完成依赖注入;还有一点,因为Dagger2是使用生成代码来实现依赖注入的,所以可以在相关代码处下断点进行调试.

1.3 Dagger2的用处

使用Dagger2最本质的目的是为了模块间解耦,提供代码的健壮性和可维护性.

举个简单的例子

public class A{

    B b;

    public A(){

        b = new B()

    }

}

如上代码,在ClassA对ClassB有个依赖,如果某一天ClassB的创建方式发生改变,即构造方法发生改变,这时不仅要修改ClassB中的代码,还需要在所有创建ClassB的类中进行修改,如上面的ClassA.

如果使用Dagger2的话,在ClassA中可以这样写:

public class A{

    @Inject

    B b;

}

此时不管B的创建方式如何修改,在ClassA中都不需要进行修改.


2.Dagger2的原理



如上图所示:

最左边,是提供依赖的部分,可以通过使用@Inject注解其构造方法或者在Module中使用@Provides注解提供.

中间部分是Component,它本质上是被@Component注解的接口,通过@Component注解与Module建立联系,并提供inject()函数.它是一个桥梁,负责连接提供依赖的部分和需要依赖的部分,

最右边是需要依赖的地方,使用@Inject注解变量,就可以创建它的对象.


3.Dagger2的使用

3.1 Android studio中配置

在工程的build.gradle文件中添加android-apt插件

dependencies {

     ... // 其他classpath

     classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' //添加apt命令

 }

module的build.gradle添加

apply plugin: 'com.neenbedankt.android-apt'//添加apt命令

dependencies {

    apt 'com.google.dagger:dagger-compiler:2.16' //指定注解处理器

    compile 'com.google.dagger:dagger:2.16'  //dagger公用api

    provided 'org.glassfish:javax.annotation:10.0-b28'  //添加android缺失的部分javax注解库

}

但是当Android studio升级到3.0之后,就会出现以下错误:

Error:android-apt plugin is incompatible with the Android Gradle plugin.  Please use 'annotationProcessor' configuration instead.

解决办法:Studio升级到3.0之后原来的配置方式apt与最新版本Gradle已经不兼容,推荐使用annotationProcessor

只需要添加这三个依赖就可以,工程的build.gradle与app的build.gradle插件可不用添加

首先在Android studioModule的Gradle中添加如下代码:

// Add Dagger dependencies

dependencies {

  compile 'com.google.dagger:dagger:2.16'

  annotationProcessor 'com.google.dagger:dagger-compiler:2.16'

 compile 'org.glassfish:javax.annotation:10.0-b28' //添加android缺失的部分javax注解库

}


3.2 常用注解

@Inject:

通常在需要依赖的地方使用这个注解。换句话说,你用它告诉Dagger这个类或者字段需要依赖注入。这样,Dagger就会构造一个这个类的实例并满足他们的依赖。

@Module:

Modules类里面的方法专门提供依赖,所以我们定义一个类,用@Module注解,这样Dagger在构造类的实例的时候,就知道从哪里去找到需要的 依赖。modules的一个重要特征是它们设计为分区并组合在一起(比如说,我们的app中可以有多个组成在一起的modules)

@Provide:

在modules中,我们定义的方法是用这个注解,以此来告诉Dagger我们想要构造对象并提供这些依赖。

@Component:

Components从根本上来说就是一个注入器,也可以说是@Inject和@Module的桥梁,它的主要作用就是连接这两个部分。

Components可以提供所有定义了的类型的实例,比如:我们必须用@Component注解一个接口然后列出所有所提供的依赖.

@Qualifier:

要作用是用来区分不同对象实例

@Named 其实是@Qualifier的一种实现


3.3 简单示例

被需要依赖的类

public class AManager {

    public void makeShow() {

        Toast.makeText(AppUtils.getApp(), "Hello Dagger2!!!", Toast.LENGTH_SHORT).show();

    }

}

提供依赖的类:Module

@Module

public class ManagerModule {    

    @Provides

    public AManager provideManagerA(String name) {

        Log.e("TAG", "provideManagerA");

        return new AManager();

    }

}

中间桥梁:Contenent

@Component(modules = {ManagerModule.class})

public interface ManagerComponent {

    void inject(MainActivity activity);

}

需要依赖的类:Container

public class MainActivity extends AppCompatActivity {

    @Inject

    AManager aManager;

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

        DaggerManagerComponent.create().inject(this);

        findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {

            @Override

            public void onClick(View view) {

                aManager.makeShow();

            }

        });

    }

}

3.4 关于模块化的实现

在项目中,出于更加灵活以及可维护等其他原因,常进行模块化的划分,比如B功能的实现需要A,C功能的实现也需要A,那么我们就需要单独对A进行处理,以便于其他功能模块的使用.

Dagger有三种途径进行模块之间的依赖:

方式一:Module中通过@Module注解,includes其他的Module

@Module(includes = {xxxModule.class})

方式二:Component中通过@Component注解关联需要的其他Module

@Component(modules = {ManagerModule.class,xxxxModule.class})

方式三:Component中通过@Component注解dependencies 其他Component

@Component(modules = {ManagerModule.class},dependencies = {xxxComponent.class})

注意事项:

通过方式三与其他模块关联时,被需要依赖的类的构造方法需要使用@Inject进行注解,同时在注入Container的时候把其他的Component进行注入.

3.5 创建和区分不同实例

>>>>>>>方式一:使用@Named注解

@Module

public class ManagerBModule {

    @Named("release")

    @Provides

    public BManager provideManagerB_Release(AManager aManager) {

        BManager bManager = new BManager(aManager);

        Log.e("TAG", "provideManagerB_Release:" + bManager);

        return bManager;

    }

    @Named("debug")

    @Provides

    public BManager provideManagerB_Debug(AManager aManager) {

        BManager bManager = new BManager(aManager);

        Log.e("TAG", "provideManagerB_Debug:" + bManager);

        return bManager;

    }

}

    @Named("release")

    @Inject

    BManager bManager_release;


    @Named("debug")

    @Inject

    BManager bManager_debug;


>>>>>>>方式二:使用自定义注解

模仿Named注解,进行自定义

自定义Release注解:

@Qualifier

@Documented

@Retention(RUNTIME)

public @interface Release {

}

自定义Debug注解:

@Qualifier

@Documented

@Retention(RUNTIME)

public @interface Debug {

}

在Module中使用:

@Module

public class ManagerBModule {

    @Release

    @Provides

    public BManager provideManagerB_Release(AManager aManager) {

        BManager bManager = new BManager(aManager);

        Log.e("TAG", "provideManagerB_Release:" + bManager);

        return bManager;

    }


    @Debug

    @Provides

    public BManager provideManagerB_Debug(AManager aManager) {

        BManager bManager = new BManager(aManager);

        Log.e("TAG", "provideManagerB_Debug:" + bManager);

        return bManager;

    }

}

在Activity中的使用:

    @Release

    @Inject

    BManager bManager_release;


    @Debug

    @Inject

    BManager bManager_debug;


3.6 Singleton单例讲解

使用@Singleton注解,需要注意的是如果Module中,@Provides注解的方法使用了@Singleton注解,那么与之对应的Component也要加上@Singleton注解.

Module代码:

Component代码:

Activity代码:

点击按钮后的Log日志:

注意事项:

Dagger2中的单例与Java中的单例不同,Java中的单例对象是存放在静态区的,而Dagger2的单例是与Component相关联的.不同Component所提供的实例化的单例对象是不同的.

3.7关于自定义Scope

由于component的dependencies与component自身的scope不能相同,即组件之间的scope不同,因此需要自定义一些Scope

对于应用级的AppComponent常使用@Singleton,以使某些对象在整个程序中只实例化一次

Module:

AppModule

Component:

AppComponent


Application:

Application


自定义的ActivityScope

ActivityScope

依赖AppComponent的Component:

依赖AppComponent的Component

Activity代码:

Activity代码

关于Scope推荐一篇文章:Dagger2Scope讲解

有关Scope注解参照@Singleton注解

@Scope :注明是Scope ,表示作用域范围

@Documented :标记在文档

@Retention(RUNTIME) :运行时级别

Scope的生命周期是依附于其容器的,当其所依附的Application或者是Activity销毁后,其关联的Module中的实例化对象也会被销毁.


3.8关于Subcomponent

在Dagger2中,Component之间存在两种关系,依赖关系和继承关系

依赖关系:一个 Component 依赖其他 Component 公开的依赖实例,使用 @Component 注解中的dependencies进行声明.

继承关系:一个 Component 继承某个Component 提供更多的依赖,SubComponent 就是继承关系的体现.

两者之间的比较:

>相同点:

    两者都能复用其他 Component 的依赖

    有依赖关系和继承关系的 Component 不能有相同的 Scope

>不同点:

      依赖关系中被依赖的 Component 必须显式地提供公开依赖实例的接口,而 SubComponent 默认继承 parent Component 的依赖.

      依赖关系会生成两个独立的 DaggerXXComponent 类,而 SubComponent 不会生成 独立的 DaggerXXComponent 类.

示例:

父Component的Module:

父Component:

子Component的Module相关:

工具类
子ComponentModule类

子Component:

Activity代码:

注意事项:

1>Subcomponent同时具备两种不同生命周期的scope, SubComponent具备了父Component拥有的Scope,也具备了自己的Scope.

2>SubComponent的Scope范围小于父Component


3.9拓展-关于Lazy与Provider:

Lazy(延时注入):

有时当需要注入的依赖在使用时再完成初始化,加快加载速度,就可以使用注入Lazy<T>.只有在调用 Lazy 的 get() 方法时才会初始化依赖实例注入依赖.

好比component初始化了一个present对象,然后放到一个池子里,需要的时候就get它,所以你每次get的时候拿到的对象都是同一个;并且当你第一次去get时,它才会去初始化这个实例.

provider(强制加载):

有时候不仅仅是注入单个实例,我们需要多个实例,这时可以使用注入Provider<>,每次调用它的 get() 方法都会调用到 @Inject 构造函数创建新实例或者 Module 的 provide 方法返回实例。

注意事项:

要想调用Porvder的get()到的实例对象不同,必须其Module中提供实例的方法没有被@Scope注解,不然所获取到的是同一对象.

4 注意事项

1. Component 的 inject(自定义,可以随便起名) 方法如果接收的是父类型参数,而调用时传入的是子类型对象则无法实现注入;

2. Component中使用@Component注解关联的modules中的Module内不能有重复的provide,除非使用@Qualifier注解进行限定,如@Named注解;

3. Module的@Provides注解的方法使用了 Scope ,那么与其关联的Component 就必须使用同一个Scope注解;如果Module中@Provides注解的方法没有使用Scope,那么与其关联的Component是否加Scope注解都无所谓,可以通过编译;

4.Component中使用@Component注解声明的dependencies与Component自身的Scope不能相同,即组件之间的Scope必须不同;

5.@Singleton注解的组件不能依赖其他Scope的组件,只能其他Scope的组件依赖Singleton的组件;

6.没有Scope的Component不能依赖有Scope的Component;

7.一个Component不能同时有多个scope(Subcomponent除外);

8.@Singleton 的生命周期依附于Component,同一个Module provide singleton实例对象 ,不同Component 也是不一样的;

9.已使用Component注入的Container不能被其他Component二次注入.









 

推荐阅读更多精彩内容

  • 巴娜娜是一只2014年12月30日出生的小萨摩。 它的妈妈是我同学的狗狗,名叫憨憨。 狗爸爸也是只纯萨摩。 巴娜娜...
    凌雪懿阅读 156评论 0 3
  • 可以输入
    dfd48e1e3fea阅读 46评论 0 1
  • 焦点网络初级 程玲玲 郑州 坚持分享第127天 出轨路线图:从可能到事实 通常,长期且有承诺关系的伴侣会在他们和世...
    思小念052阅读 43评论 0 0