Dagger2神器入门(二)

作者:小腊月

地址:http://www.jianshu.com/p/c673e6e73c8b

声明:本文是小腊月原创,已获其授权发布,未经原作者允许请勿转载

前言

在Dagger2神器入门(一)中,我们了解了什么是依赖注入,那么在这一章中,我们将逐渐入门Dagger2。接下来我们会通过demo形式给大家展现Dagger2的神奇之处。

上文中通过"开车上班"的例了解了什么是"依赖注入",那么我们继续这例子。我们知道车由发动机,轮子,车座等部件组成。那么,如果我们要造一辆车的话,可能需要这些部件。回归到代码中,我们new一个Car可能需要发动机,轮子,车座等对象。

原始依赖

首先,我们创建造车所需要的部件。 发动机

/**

 * 发动机
 */
public class Engine {

public Engine(){
    Log.d(Config.TAG,"new Engine()");
  }
}

车座

/**

 * 车座
 */
public class Seat {
    public Seat(){
        Log.d(Config.TAG,"new Seat()");
    }
}

轮子

/**

 * 轮子
 */
public class Wheel {
    public Wheel(){
        Log.d(Config.TAG,"new Wheel()");
    }
}

上面3个类用到了Config配置类,其实就是一个字符串。

public class Config {

public static final String TAG = "TAG"; }

另话:把公共的部分抽取出来,也是代码规范的一部分。在今后的工作中,需要不断review自身代码,随着技术水平的提高,代码质量也需要不断提高。

上面的代码中,我们写了3个类,都是用来造车的构件。那么对于造车,对应于我们的代码就是new Car(),就是这么简单。但是Car可能需要Engine,Seat,Wheel 等组件。那么我们来造个车试试。

public class Car {

    private Engine engine;
    private Seat seat;
    private Wheel wheel;
    public Car() {
        engine = new Engine();
        seat = new Seat();
        wheel = new Wheel();
        Log.d(Config.TAG, "new Car()");
    }
}

按照正常逻辑,Car类应该是这样写。那么我们在new Car()试试。

06-27 12:44:53.726 18967-18967/com.bae.basicandext D/TAG: new Engine()

06-27 12:44:53.726 18967-18967/com.bae.basicandext D/TAG: new Seat() 06-27 12:44:53.726 18967-18967/com.bae.basicandext D/TAG: new Wheel() 06-27 12:44:53.726 18967-18967/com.bae.basicandext D/TAG: new Car()

就这样,我们把Car给new出来了,这样写也是没有问题的。 那么接下来我们用Dagger2的方式,来做做试试,先不管他们之间的区别,just do it。做出来效果之后再回过头来反思。

Dagger2依赖注入

下面让我们一步一步走下去:

1 在modle的build.gradle文件中添加

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

compile 'com.google.dagger:dagger:2.0.2' provided 'org.glassfish:javax.annotation:10.0-b28'

2 写一个Module类,管理上面的三个依赖。

@Module

public class CarModule {
    @Provides
    public Engine provideEngine(){
        return new Engine();
    }

    @Provides
    public Seat provideSeat(){
        return new Seat();
    }

    @Provides
    public Wheel provideWheel(){
        return new Wheel();
    }
}

3 写一个Component类,来连接Module和你的Car。

@Component(modules = CarModule.class)

public interface CarComponent {
  void inject(Car car);
}

4 重写Car类

public class Car {


  @Inject
  Engine engine;
  @Inject
  Seat seat;
  @Inject
  Wheel wheel;
//    private Engine engine;
//    private Seat seat;
//    private Wheel wheel;

  public Car() {
//        engine = new Engine();
//        seat = new Seat();
//        wheel = new Wheel();
    DaggerCarComponent
            .builder()
            .carModule(new CarModule())
            .build()
            .inject(this);
    Log.d(Config.TAG, "new Car()");

  }
}

看看输出

06-27 13:03:25.447 26227-26227/com.bae.basicandext D/TAG: new Engine()

06-27 13:03:25.447 26227-26227/com.bae.basicandext D/TAG: new Seat() 06-27 13:03:25.447 26227-26227/com.bae.basicandext D/TAG: new Wheel() 06-27 13:03:25.447 26227-26227/com.bae.basicandext D/TAG: new Car()

是不是达到了和之前一样的效果呢?是不是从第3步开始,就不知道为什么这样写了呢?

分析

我们看看CarModule类是用一个@Module注解的类,里面的方法是使用@Provides注解。什么意思呢? @Moudle 表示该类能够管理并提供依赖;你需要造车,但是车依赖于发动机,轮胎以及车座,那么写一个@Module注解的类来帮你管理这些依赖。 @Provides 表示该方法提供依赖;通过这个注解的方法,能给你提供依赖,看代码应该清楚。

我们知道了管理并提供依赖的类,那么我们就可以通过它来直接使用依赖。但是Dagger2为了解耦,提供了一个中介,@Component注解,也就是我们的第4步。

@Component(modules = CarModule.class)

public interface CarComponent {
     void inject(Car car);
  }

我们要清楚,@Component就是一个中间人,里面存着依赖提供者和依赖需求者。在这里

@Component(modules = CarModule.class

表示的是需要在CarModule类中去寻找依赖,void inject(Car car);这个方法是抽象的,表示需要将这些依赖应用到Car类。说白了就是Car类需要CarModule来提供依赖。

那么我们来看看@Component的官方文档。

* Annotates an interface or abstract class for which a fully-formed,

dependency-injected * implementation is to be generated from a set of {@linkplain #modules}.

说的是这个注解只能用于接口或者抽象类。将代码改成下面,输出也是一样的。

@Component(modules = CarModule.class)

public abstract class CarComponent {
    public abstract void inject(Car car);
}

在上面的步骤中,我们搞定了依赖提供者,中间人,现在我们要看看依赖需求者。在这我们的需求者是Car,也就是上面写的Car类。

我们用到了@Inject注解,

@Inject

Engine engine;

上面的代码表示engine这个属性你不用像一般情况去初始化(engine= new Engine()),它能给你自动寻找依赖。但是如果是这样肯定是不行的,还需要

DaggerCarComponent

.builder() .carModule(new CarModule()) .build() .inject(this);

DaggerCarComponent是apt工具帮我们生成的类,实现了CarComponent接口。 通过carModule()将我们的依赖提供者传入,通过inject()将我们的Car对象传入,这样就达到了中间人的目的。

@Generated("dagger.internal.codegen.ComponentProcessor")

public final class DaggerCarComponent implements CarComponent {
     private Provider<Engine> provideEngineProvider;
     private Provider<Seat> provideSeatProvider;
     private Provider<Wheel> provideWheelProvider;
     private MembersInjector<Car> carMembersInjector;

    private DaggerCarComponent(Builder builder) {  
        assert builder != null;
        initialize(builder);
    }

    private void initialize(final Builder builder) {  
        this.provideEngineProvider = CarModule_ProvideEngineFactory.create(builder.carModule);
        this.provideSeatProvider = CarModule_ProvideSeatFactory.create(builder.carModule);
        this.provideWheelProvider = CarModule_ProvideWheelFactory.create(builder.carModule);
        this.carMembersInjector = Car_MembersInjector.create(provideEngineProvider, provideSeatProvider, provideWheelProvider);
}

    public static Builder builder() {  
        return new Builder();
      }
      public static CarComponent create() {  
        return builder().build();
      }
     @Override
      public void inject(Car car) {  
          carMembersInjector.injectMembers(car);
      }
}

上面的代码是自动生成的。也就是你在CarComponent接口中加了@Component注解,然后注解处理器(dagger.internal.codegen.ComponentProcessor)就能帮你生成上面的代码,要不然程序如何知道你的注解是什么意思?

后记

如果对于注解相关知识不太了解,可以看看《Java编程思想》注解那一章。我提两点: 1 不是所有注解都用到了反射,只有@Retention(RUNTIME)才可能会用到反射。关于什么是反射,这里有涉及到类加载机制,不明白的可以翻翻我之前的blog。 2 自定义注解都需要注解处理器来处理的,不然你随便定义一个注解,谁能明白?就像上面的ComponentProcessor类一样,处理@Component注解。

这章主要是入门了Dagger2,下面我们会了解 1 为什么要使用Dagger2来替代文章一开头的写法? 2 如果@Inject注解的构造器有多个怎么办? 3 如果存在依赖链怎么办呢?

Tips

学习不要贪多,一点一点的消化,逐个击破,你会发现原来自己会的很多。比如讲Dagger的时候,我们会用到注解,那就得去了解一下注解相关知识;一提到注解很多人就会想到反射,那就要去看看反射的内容;反射里面涉及了类加载机制,又可以看看JVM相关的知识。学着学着,你会发现所有的内容都是相关的,这就是一种学习的境界了。不断的联想,不断的巩固,才能不断的提高。

原文发布于微信公众号 - Android先生(cyg_24kshign)

原文发表时间:2017-10-25

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏移动开发之家

Android蹲坑的疑难杂症集锦(兼Gradle) 二

 这次我们继续聊聊项目中遇到的问题吧,作为上辈子的天使,才换来今生的入坑Android,码农不易,我们的梦想就是,下辈子下班时,不再月上柳梢头,可以看到夕阳下的...

12040
来自专栏一个会写诗的程序员的博客

Kotlin 使用 Spring WebFlux 实现响应式编程 Kotlin 使用 Spring WebFlux 实现响应式编程参考资料

IBM的研究称,整个人类文明所获得的全部数据中,有90%是过去两年内产生的。在此背景下,包括NoSQL,Hadoop, Spark, Storm, Kylin在...

19740
来自专栏杨建荣的学习笔记

缓慢的update语句性能分析(r6笔记第61天)

最近处理一个问题的时候,先是收到DB time升高的报警,然后查看DB time的情况发现,已经有近1000%的负载了。 ? 带着好奇心想看看到底是什么样的...

30750
来自专栏A周立SpringCloud

使用Spring Cloud Feign上传文件

最近经常有人问Spring Cloud Feign如何上传文件。有团队的新成员,也有其他公司的兄弟。本文简单做个总结—— 早期的Spring Cloud中,Fe...

43190
来自专栏微信音视频小程序

教你1天搭建自己的“微视”

A simple iOS Application project is shown below to illustrate how to configure S...

1K50
来自专栏bboysoul

使用永恒之蓝(MS17-010)漏洞入侵windows7

前几天,被勒索病毒(Wannacry)刷屏了,这个病毒是大家知道了备份文件的重要性,当然,我是不怕什么病毒的,因为我是一天备份一次文件,而且微云一份,坚果云,移...

3.2K40
来自专栏数据库新发现

Oracle数据库恢复:归档日志损坏案例一则

链接:http://www.eygle.com/archives/2010/11/recover_archivelog_corruption.html

11320
来自专栏Android中高级开发

Android开发之漫漫长途 IX——彻底掌握Binder

该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列。该系列引用了《Android开发艺术探索...

12420
来自专栏杨建荣的学习笔记

一个Oracle bug的手工修复(r6笔记第59天)

在上周五的时候,本来一个例行巡检,想扩充一些表空间,结果弄巧成拙,因为一个drop datafile的操作直接导致了一主两备的两个备库MRP直接抛出了ORA-6...

29550
来自专栏耕耘实录

找回win7桌面IE图标我有绝招

现在很多同学都用了微软的新一代操作系统Windows7,都为Windows7的华丽界面及更人性化得操作所深深吸引,但是由于我们大多数同学都习惯了原来的Windo...

37050

扫码关注云+社区

领取腾讯云代金券