Dagger2 使用总结(一)

Dagger2是一套依赖注入框架,在编译期间自动生成代码,创建依赖的对象。项目中使用Dagger2可以降低代码的耦合度。
使用Dagger2库,重点是了解其中的各种注解并熟练使用,下面看一下具体用例。

注:为了能够更直观地了解Dagger2的使用,本文用例会尽量简化不相关的业务逻辑

环境配置


配置下build.gradle即可

dependencies {
    implementation 'com.google.dagger:dagger:2.14.1'
    annotationProcessor 'com.google.dagger:dagger-compiler:2.14.1'
}

 

@Inject和@Component注解


使用@Inject@Component注解是Dagger2最基本的使用方式,来看一个简单的Demo,向 MainActivity 里注入 Apple

 

  • 编写 Apple类代码
public class Apple {

    @Inject   //构造器注入注解,在被注入对象的构造器上标注
    public Apple() {

    }

    public void print() {
        Log.d(TAG,"This is an apple");
    }
}

 

  • 编写 MainActivity 类代码
@Inject //属性注入注解,在需要注入的属性上标注,不能为private,如果需要用private可以使用方法注入,下面会讲
public Apple apple;

@Component  //用于连接注入类和目标类的接口,建议命名:目标类名+Component
interface MainActivityComponent {
    void inject(MainActivity activity);
}

 

  • 编译项目

这时候编译项目会在build目录下生成以下文件:
Apple_Factory.java
DaggerMainActivity_MainActivityComponent.java
MainActivity_MembersInjector.java
这些文件是Dagger2框架帮我们自动生成的,用于帮我们注入依赖。

 

  • MainActivity 注入依赖

使用依赖注入

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

    //给之前定义的apple注入依赖
    DaggerMainActivity_MainActivityComponent.builder().build().inject(this);
    apple.print();

}

如果不使用依赖注入,那写法将会是下面这样:

//直接new出对象,耦合性高,在项目较复杂的情况下扩展性较差
apple = new Apple();

这样看起来比不使用Dagger2注入对象要简单很多,但是如果对象引用的地方很多,构造复杂,那么一旦改变构造,工作量会很大,这也是Dagge2最重要的优势——解耦。

  • 小结

以上是Dagger2最简单的使用方法,注入依赖后,会用在Apple类中标注@Inject注解的构造器自动创建apple对象。而@Component标注的接口用于连接目标类和注入类的工厂Apple_Factory

  • 方法注入

如果注入的对象需要设置为private,那我们可以使用方法注入,示例:

private Apple apple;

@Inject
public void setApple(Apple apple) {
    this.apple = apple;
}

上述代码作用等同于:

@Inject
public Apple apple;
  • 拓展

如果现在需要修改Apple类的构造器,需要加一个Color参数,如下:

public class Apple {

    public Color color;

    @Inject
    public Apple(Color color) {
        this.color = color;
    }

    public void print() {
        Log.d(TAG, "This is an apple");
    }
}

这时候构造器中的color也需要注入依赖,创建一个简单的Color类并在构造器上标注注解@Inject即可

public class Color {
    @Inject
    public Color() {

    }    
}

这样在构建apple对象时会寻找标注了@Inject注解的Color的构造器新建color对象用于apple对象的构造。
除了@Inject@Component之外,Dagger2中还有其他注解,下文会说明。

 

@Modele和@Provides注解


@Inject注解存在局限性,以下两种情况时不能使用:

  • 注入的对象来自第三方库
  • 注入的对象声明为抽象类或接口(依赖倒置原则)

比如上述的例子修改下,添加一个抽象类Fruit

public abstract class Fruit {
    public abstract void print();
}

修改后Apple类继承Fruit

public class Apple extends Fruit {

    @Inject
    public Apple() {

    }

    @Override
    public void print() {
        Log.d(TAG,"This is an Apple");
    }
}

这时候,如果只按照下面的形式声明,是无法注入apple对象的,运行会报错。

@Inject
public Fruit apple;

这时候就需要用到@Modele@Provides注解,完整示例如下:

  • 创建Fruit类和Apple

Fruit抽象类:

public abstract class Fruit {
    public abstract void print();
}

Apple类:

public class Apple extends Fruit {

    //这里注意可以不再标注@Inject
    public Apple() {

    }

    @Override
    public void print() {
        Log.d(TAG,"This is an Apple");
    }
}

 

  • 创建Module类
@Module //注解提供注入对象的类
public class FruitModule {
    @Provides //注解方法,方法返回注入的对象
    Fruit provideFruit() {
        return new Apple();
    }
}

 

  • MainActivity中注入对象
@Inject //和之前一样的注解
public Fruit apple;

@Component(modules = FruitModule.class) //这里注意,需要加上Module类
interface MainActivityComponent {
    void inject(MainActivity activity);
}

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

    //和之前一样的方式注入对象
    DaggerMainActivity_MainActivityComponent.builder().build().inject(this);
    apple.print();
}

 

  • 小结

使用@Inject@Component注解注入对象时,我们需要在声明的注入类的构造器上加上@Inject注解来使对象被创建。
如果遇到注入类构造器不方便编辑时(比如来自三方库,抽象类,接口),则可以使用@Module@Provides注解,将对像获得由构造器获得转变为由方法返回,使对象的创建形式更加可控。

再形象看一下两者的区别(不想看可以跳过):

仅使用@Inject@Component注入对象build目录下生成的文件如下
Apple_Factory.java
DaggerMainActivity_MainActivityComponent.java
MainActivity_MembersInjector.java

使用@Module@Provides注入对象build目录下生成的文件如下
FruitModule_ProvideAppleFactory
DaggerMainActivity_MainActivityComponent.java
MainActivity_MembersInjector.java

区别在于前者是Apple_Factory.java而后者是FruitModule_ProvideAppleFactory,这两个类都是用于提供对象的,而DaggerMainActivity_MainActivityComponent.java类实现了MainActivityComponent接口,用于将目标类和提供对象的类连接起来。所以两者的区别仅在于提供对象的方式不同,再具体可以看这两个差异类的实现。

  • 拓展

Component接口可以指定多个Module类,便于将它们一起注入,比如在这个例子中,可以按下述方式编写:

@Component(modules = {FruitModule.class, OtherModule.class}) 
interface MainActivityComponent {
    void inject(MainActivity activity);
}

这样我们便可以注入OtherModule中可以获得的对象。

 

@Named注解


上述的@Module@Provides注解仅能返回一个继承了Fruit的对象,如果再加入一个Banana类,继承Fruit并在MainActivity中注入,这时候就需要使用@Named注解。基于上述代码,示例如下:

  • 新建Banana
public class Banana extends Fruit {
    @Override
    public void print() {
        Log.d(TAG, "This is a Banana");
    }
}

 

  • 修改FruitModule
@Module
public class FruitModule {

    @Provides
    @Named("Apple") //用@Named注解标注返回Apple的方法
    Fruit provideApple() {
        return new Apple();
    }

    @Provides
    @Named("Banana") //用@Named注解标注返回Banana的方法
    Fruit provideBanana() {
        return new Banana();
    }

}

 

  • 修改MainActivity
@Inject
@Named("Apple")
public Fruit apple;  //用@Named注解标注这个声明注入的为Apple

@Inject
@Named("Banana")
public Fruit banana; //用@Named注解标注这个声明注入的为Banana

@Component(modules = FruitModule.class)
interface MainActivityComponent {
    void inject(MainActivity activity);
}

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

    DaggerMainActivity_MainActivityComponent.builder().build().inject(this);
    apple.print();
    banana.print();
}

 

  • 小结

@Named注解使用比较简单,只要将Module类中的标注和目标类中声明注入类的标注一一对应即可。

 

@Qualifier注解


@Qualifier注解的作用和@Named相同,只是实现有所区别,直接看示例:

  • 修改FruitModule类
@Module
public class FruitModule {

    @Qualifier //定义Apple的注解
    @Retention(RetentionPolicy.RUNTIME)
    public @interface ProvideApple{}

    @Qualifier //定义Banana的注解
    @Retention(RetentionPolicy.RUNTIME)
    public @interface ProvideBanana{}

    @Provides
    @ProvideApple  //表示这个方法返回Apple
    Fruit provideApple() {
        return new Apple();
    }

    @Provides
    @ProvideBanana //表示这个方法返回Banana
    Fruit provideBanana() {
        return new Banana();
    }

}

 

  • 修改MainActivity类
@Inject
@FruitModule.ProvideApple  //表示这个对象注入Apple
public Fruit apple;

@Inject
@FruitModule.ProvideBanana //表示这个对象注入Banana
public Fruit banana;

@Component(modules = FruitModule.class)
interface MainActivityComponent {
    void inject(MainActivity activity);
}

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

    DaggerMainActivity_MainActivityComponent.builder().build().inject(this);
    apple.print();
    banana.print();
}

 

  • 小结

@Qualifier@Named注解作用是一样的,区别是@Named使用字符串来区分不同的返回对象,而@Qualifier用自定义的接口来区分,这样可以提高代码的可读性,且不容易出错。

 
内容有点多,不得不分成两篇总结了。
贴个链接:Dagger2 使用总结(二)

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

推荐阅读更多精彩内容

  • 部分内容参考自:[Android]使用Dagger 2依赖注入 - DI介绍(翻译)[Android]使用Dagg...
    AItsuki阅读 47,207评论 66 356
  • ****(说在最前:阅读本篇之前,希望大家对Dagger2已经有了一个初步的了解。从而帮助感觉似是而非的同学进一步...
    我是昵称阅读 903评论 3 6
  • Dagger2 入门 2016-12-21 更新:添加@Subcomponent注解以及Lazy与Provider...
    fxzou阅读 28,523评论 77 331
  • 几个问题 看到Dagger2这个词的时候,相信很多人会有很多的疑问如下:Dagger2如何使用在例子中?Injec...
    codeHoward阅读 7,008评论 4 10
  • 比赛结束了,感觉也不是很好吧,抽签就比较差,最难的就是等待结果的过程。因为结果你是没办法去决定的,不知道结果之后应...
    冬天的帽子阅读 139评论 0 0