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二次注入.









 

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,254评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,875评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,682评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,896评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,015评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,152评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,208评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,962评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,388评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,700评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,867评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,551评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,186评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,901评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,142评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,689评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,757评论 2 351

推荐阅读更多精彩内容

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