Dagger2系列----配置以及基本概念讲解

前言

Dagger2 当前Android流行的依赖注入框架。未来Android应用的趋势也许是MVP+Retrofit+Dagger+RxJava结合其他高效简便的库来开发。Dagger2更好的解耦了代码。出于兴趣,前段时间开始学习Dagger2依赖注入框架。通过学习前人的文章以及自己写Demo,成功地敲开了Dagger2的大门并了解了Dagger2实现依赖注入的原理。感谢各位前辈的分享,在这里抛砖引玉的讲讲我理解的Dagger2。 一来希望可以帮助像我一样刚刚开始接触Dagger2的小菜鸟更好的入门,二来,以后自己需要用到的时候也可以翻来看看。

好啦,闲话不多扯,上文。

Dagger2 配置

配置Dagger2开发环境很简单。只需两步。

  • 在工程的build.gradle 加入的dependencies中加入 classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'.apt是用于自动生成代码的,Dagger2需要依赖它自动生成代码,下面你就知道了。
  dependencies {
        classpath 'com.android.tools.build:gradle:2.2.0'
//引入APT,用于自动生成代码
        classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'

    }

注意: 是在工程项目的build.gradle中添加,别添加错了_

  • 在项目即modlue中的build.gradle添加四句话:
    1. apply plugin: 'com.neenbedankt.android-apt'(添加在顶部)
    2. apt 'com.google.dagger:dagger-compiler:2.0'
    3. compile 'com.google.dagger:dagger:2.0'
    4. compile 'javax.annotation:javax.annotation-api:1.2'
    5. compile 'javax.inject:javax.inject:1'
      后面三句添加在dependencies中。
apply plugin: 'com.android.application'
//引入APT
apply plugin: 'com.neenbedankt.android-apt'
       .
       .
       .
dependencies {
    //引入Dagger2以及依赖的一些包,因为用到注释所以还需要引用annotation包
    apt 'com.google.dagger:dagger-compiler:2.0'
    compile 'com.google.dagger:dagger:2.0'
    compile 'javax.inject:javax.inject:1'
    compile 'javax.annotation:javax.annotation-api:1.2'
}

好啦,在上述文件,添加了依赖和相关配置之后,你就可以开始使用Dagger2了。
。。。。等等,还没告诉我们怎么用呢,怎么就开始了。小编,疏忽。接下来,先给大家讲讲Dagger2的基本概念和几个关键注释。最后,在用简单的例子告诉大家怎么用吧。

依赖注入

刚开始接触Dagger2的各位看官应该很好奇,为什么Dagger2能够成为主流框架之一,得万千开发者的独宠。心想肯定是有什么强大的功能。是的,Dagger2的强大功能就在于依赖注入,解耦,减少new 实例化对象的重复劳动,并且更加适应开发过程中需求的变动(也就是解耦啦!)。先用代码来解释一下什么叫做依赖注入吧。

public class A {
    B  b; 
    public A(){
      b = new B();
    }
}

上面是一个简单反映依赖现象的类A,类A需要(依赖于)B的实例,B被需要(依赖)。A通过new创建了一个B的实例。但是如果B的构造函数改变了,变成了带参数的构造函数。例如:

public class B {
  public B(String name) {
  }
}         

那么A的代码就要修改:

public class A {
    B  b; 
    String name = "name";
    public A(){
      b = new B(name);
    }
}

现在只是简单的一个A,如果你编写一个复杂的业务逻辑,许多个类都像A一样依赖于B,那么其他类也要修改,给创建B的代码中添加一个String参数,这将增加了维护和测试成本。需求总是有可能在未来的某个时刻变,每次这样的重复修改,相信你改的欲仙欲死的。
那么怎么解决耦合来更好的应变未来的改变呢,设计模式主张把容易变化抽离出来,我们可以把B的创建抽离出来。通过依赖注入的方式来传递给A。

举个栗子:School里面肯定有Teacher,没有Teacher的School就称不上学校了吧。所以School依赖于Teacher,在这个情景下呢,School就是将要被注入依赖的目标类,而Teacher呢,就是要被注入到目标类的被依赖实例。这个时候我们就可以用依赖注入把Teacher的实例给School。

依赖注入的方式有三种:

public class School implements  InjectTeacher {
    Teacher mTeacher;
    // 1.在构造函数中,直接传入一个teacher对象
    public School(Teacher teacher) {
        this.mTeacher = teacher;
    }

    // 2. set Teacher实例
    public void setTeacher(Teacher teacher ) {
        this.mTeacher = teacher;
    }
   
    // 3. 通过接口,传入一个Teacher对象。
    @Override
    public void injectTeacher(Teacher teacher) {
        this.mTeacher  = teacher;
    }
}

这三种都是依赖注入的方法,就是把Teacher的实例对象提供给School的一个中间桥梁,而在Dagger2里面也有一个这样的桥梁,他就是Componet,是一个接口。待会我们再揭开Component的面纱。其实也就是避免在Shcool里面直接new一个teacher对象。我们常用的有第一种和第二种。而Dagger2呢就是通过接口的方式来实现依赖注入的一种框架。不需要new来创建实例了,直接通过添加@Inject等注释就可以,创建一个实例对象给要注入依赖的类了。

依赖注入总结 依赖注入涉及三个对象。

  • 目标类 :需要依赖其他类。
  • 被依赖的类 : 被目标类依赖的类。
  • 实现把被依赖的类 注入到目标类的工具(中间桥梁)。

不知道这样讲,大家有没有对依赖注入有那么点了解了。还不理解看官,在查看相关的资料了解了解,我就讲到这里啦。讲的不好,勿喷,不过欢迎指正。

Dagger2 基本概念

在Dagger2 中实现依赖注入,有六个关键的注释。接下来分别给大家讲解一下这七个注释。

@injcet

用于注释目标类里面要被依赖的其他类以及其他类的构造函数,告知Dagger2,这个类要被依赖注入。
PS :@Inject注解的字段不能是privateprotected的.
原因: 在实现依赖的过程中,需要直接引用成员变量,eg.instance.mDaggerTest ,instance是目标类,mDaggerTest是目标类里面一个被依赖的其他类的实例对象,因为需要直接引用,所以不可以是privateprotected,否则报错。

  • 代码范例:
 //目标类
public class MainActivity extends Activity {
    @Inject
    DaggerTest  mDaggerTest;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

}

//被依赖的类

public class DaggerTest {
    @Inject
    public DaggerTest() {
        Log.e("Dagger", "Dagger1 Test");
    }

    public void log(){
        Log.e("Dagger", "Dagger2 Test");
    }
}

@module

@module用于注释自定义的产生依赖实例的类,可以看成是一个简单的工厂类,用于创建类的实例。Dagger2在实现依赖注入时,比如要实例化一个A类的对象,通过查找@moudle注释的类中返回A类对象的方法,来实例化一个A类的对象,从而实现依赖注入。

  • 为什么要有module?不是已经有了inject吗?

    存在的就是合理的嘛。在介绍inject使用的方法时,我们提到inject是在目标类的 成员变量定义的时候注释,以及在被依赖的类的构造方法上注释。但是有时候我们需要依赖的是第三方库的时候,inject就不适用了,因为我们不能在第三方库的类的构造函数上面添加inject。那怎么办呢?这个时候就需要用到module了,module就是给第三方库提供依赖的一个工厂类。当然不是第三方库的类,也可以通过module注释的类来提供。

  • 代码范例:

 @Module
public class DaggerModules {
    private DemoApplication mDemoApplication;
    
    public DaggerModules(DemoApplication demoApplication) {
        mDemoApplication = demoApplication;
    }
    
    @Provides
    @Singleton
    Context getApplicationContext() {
        return mDemoApplication;
    }
    
    @Provides
    @Singleton
    LocationManager provideLocationManager() {
        return (LocationManager) mDemoApplication.getSystemService(Context.LOCATION_SERVICE);
    }
}

关于module注解的类应该如何编写,在下一篇分析Dagger2的依赖注入实现的代码分析将作详细的分析。敬请期待。。。

@Provider

在@moudle注释的类里面,标记方法的注释。与Component建立连接,实现依赖注入。

@Component

@Component 标记的类相当于依赖注入里面的注入器,是实现依赖注入的中间桥梁,将被依赖类的实例注入到目标类中。

  • Component在将被依赖的类注入到目标类的时候,是怎么判断需要的是哪个类型的实例对象呢?

Component是通过@inject 或者 @Provider注释的构造函数或者方法的返回值的类型来确定是否是要注入的类的实例的提供方法。所以返回值的类型非常重要,一定要和目标类里面标注的类型一样。

@Qualifier

  • 在上面,提到了Component是通过返回值类型来确定要调用的方法的,那么如果出现下面这种情况呢?
  @Module
public class DaggerModules {
    private DemoApplication mDemoApplication;
    
    public DaggerModules(DemoApplication demoApplication) {
        mDemoApplication = demoApplication;
    }
    
    @Provides
    Teacher getWomanTeacher() {
        return new WomanTeacher();
    }
    
    @Provides
    Teacher getManTeacher() {
        return new ManTeacher();
    }
    
}

Component不知道如何选择了,两个方法都是返回Teacher类型,但是其实返回的对象时不同的对象,如果要依赖ManTeacher,则应该是第二个。很多人把这个现象叫Component迷失,Component只关注返回类型,所以当相同的时候,它就不知道如何选择了。

  • 那怎么解决这个问题呢?

这个时候@Qualifier就要发挥它的作用了。Qualifier正如其名,就是限定词,通过添加这个注释可以限定这个方法返回实例的类型。光说不如上代码更通俗易懂。

  • 代码范例:
//自定义两个限定词,Man与Woman。用来标示男女。
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface Man {}

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface Woman {}

将上面的代码修改之后如下:

 @Module
public class DaggerModules {
   private DemoApplication mDemoApplication;
   
   public DaggerModules(DemoApplication demoApplication) {
       mDemoApplication = demoApplication;
   }
   
   @Provides
   @Woman
   Teacher getWomanTeacher() {
       return new WomanTeacher();
   }
   
   @Provides
   @Man
   Teacher getManTeacher() {
       return new ManTeacher();
   }
   
}

在目标类中使用的时候代码如下:

 //目标类
public class MainActivity extends Activity {
   @Inject
   DaggerTest  mDaggerTest;
   
   @Inject 
   @Woman
   Teacher mTeacherWoman;
   
   @Inject
   @Man
   Teacher mTeacherMan;

   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_main);
   }

}

这样就可以区分开来啦!这里稍微提一下:@Named 是Dagger预置的一个@Qualifier的实现类。用@Named也是用于区分相同返回值类型的方法的一种注释。

  • 代码范例
  @Module
public class DaggerModules {
    private DemoApplication mDemoApplication;
    
    public DaggerModules(DemoApplication demoApplication) {
        mDemoApplication = demoApplication;
    }
    
    @Provides
    @Named(Woman)
    Teacher getWomanTeacher() {
        return new WomanTeacher();
    }
    
    @Provides
    @Named(Man)
    Teacher getManTeacher() {
        return new ManTeacher();
    }
    
}

目标类只需要把@Man改成@Named(Man),把@Woman改成@Named(Woman)就可以了。但是个人觉得直接自定义一个@Qualifier的标识比用Named好,不用担心括号里面不小心粗心大意地写错了,导致整个程序错误。

@Singleton

@Singleton是Dagger2预置的@Scope的实现类,用于标识这个实例是单例的。

@Scope

@Scope就是限定依赖的对象的,生命周期的范围的一个标识符。可以自定义自己的@Scope,例如常见的有@PerActivity,@PerApplication。而@Singleton是Dagger2预置的实现了@Scope的一个注解,代表了依赖对象时单例的。

Dagger2获取依赖实例的规则

最后我们来讲一下,Dagger2是如何获取要依赖的实例的,因为Module与Inject注解的构造函数都可以提供类的实例,那当需要实例化一个类的时候,肯定有一个规则,才不会天下大乱。Dagger2查找并实例化一个类的规则如下:

  • 步骤1:查找Module中是否存在创建该类的方法。

  • 步骤2:若存在创建类方法,查看该方法是否存在参数

    1. 步骤2.1:若存在参数,则按从步骤1开始依次初始化每个参数
    2. 步骤2.2:若不存在参数,则直接初始化该类实例,一次依赖注入到此结束
  • 步骤3:若不存在创建类方法,则查找Inject注解的构造函数,看构造函数是否存在参数

    1. 步骤3.1:若存在参数,则从步骤1开始依次初始化每个参数
    2. 步骤3.2:若不存在参数,则直接初始化该类实例,一次依赖注入到此结束

从上面的步骤可以看出来,module的优先级高于@Inject注解的构造函数。并且如果要实例化的类还依赖与其他的类,那么会先实例化其他的类,然后实例化要依赖的类。当module中找不到创建类的方法,才去找Inject注解的构造函数。

最后的最后,分享给大家一些我觉得不错的入门文档,有兴趣的可以看看,有些讲的还是不错的。 这篇文章是我第一次写的知识分享类文章,有说的不对的地方欢迎指正,多多指教,不喜勿喷。

参考文档

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

推荐阅读更多精彩内容