Skip to content

Latest commit

 

History

History
272 lines (213 loc) · 10.2 KB

2.Dagger2入门demo(二).md

File metadata and controls

272 lines (213 loc) · 10.2 KB

Dagger2入门demo(二)

Dagger中使用了很多注解:

  • @Module:Modules类里面的方法专门提供依赖,所以我们定义一个类,用@Module注解,这样Dagger在构造类的实例的时候,就知道从哪里去找到需要的 依赖。modules的一个重要特征是它们设计为分区并组合在一起(比如说,在我们的app中可以有多个组成在一起的modules)
  • @Provide:在modules中,我们定义的方法是用这个注解,以此来告诉Dagger我们想要构造对象并提供这些依赖。
  • @Singleton:当前提供的对象将是单例模式 ,一般配合@Provides一起出现
  • @Component:用于接口,这个接口被Dagger2用于生成用于模块注入的代码
  • @Inject:在需要依赖的地方使用这个注解。(你用它告诉Dagger这个构造方法,成员变量或者函数方法需要依赖注入。这样Dagger就会构造一个这个类的实例并满足他们的依赖。)
  • @Scope:Scopes可是非常的有用,Dagger2可以通过自定义注解限定注解作用域。

这个东西不太好入门,这里就直接用例子上代码了。有关如何添加以来在github上面写的很清楚了,androidbuild.gradle中进行配置就好了。

这里就用dagger官方的coffee的例子说明

官方demo描述的是:一个泵压式咖啡机(CoffeeMaker)由两个主要零件组成,泵浦(Pump)和加热器(Heater),咖啡机有一个功能是煮泡咖啡(brew), 当进行煮泡咖啡时,会按如下几个步骤进行:

  • 打开加热器进行加热
  • 泵浦加压
  • 萃取出咖啡
  • 然后关闭加热器

一杯咖啡就算制作完毕了。

好了,那我们先开始写代码了(我把官方的示例代码进行了简化,方便更好的理解):

定义加热器接口:

interface Heater {
    void on();
    void off();
}

具体实现类:

class ElectricHeater implements Heater {
 
    @Override
    public void on() {
        System.out.println("~ ~ ~ heating ~ ~ ~");
    }

    @Override
    public void off() {
        
    }
}

定义泵浦接口:

interface Pump {
    void pump();
}

具体实现类:

class Thermosiphon implements Pump {
    
    Thermosiphon() {
   
    }

    @Override
    public void pump() {
        System.out.println("=> => pumping => =>");
    }
}

咖啡机功能:

class CoffeeMaker {
    private final Heater heater;
    private final Pump pump;

    CoffeeMaker() {
        heater = new ElectricHeater();
        pump = new Thermosiphon();
    }

    public void brew() {
        heater.on();
        pump.pump();
        System.out.println(" [_]P coffee! [_]P ");
        heater.off();
    }
}

好了完成了,如果执行的话运行结果是:

~ ~ ~ heating ~ ~ ~
=> => pumping => =>
 [_]P coffee! [_]P 

完美,上面是我们普通的开发方式,那如果我们要用dagger来实现该怎么弄呢?

首先这几个类是没有改动的,因为这几个类中没有牵扯到其他的类:

interface Pump {
  void pump();
}

interface Heater {
  void on();
  void off();
}

class ElectricHeater implements Heater {

  @Override 
  public void on() {
    System.out.println("~ ~ ~ heating ~ ~ ~");
  }

  @Override 
  public void off() {
    
  }
}

下面就开始使用dagger需要改动的部分,首先创建module类。@Module类的命名惯例是以Module作为类名称的结尾。而@Module类中的@Provides方法名称的命名惯例是以provide作为前缀。 而这个Module是干什么的呢?Module管理所有的以来,这里你做咖啡需要以来泵浦(Pump)和加热器(Heater),所以可以创建Module把他们PumpHeater管理起来,方便 之后获取PumpHeater对象,所以它里面会有provideXXX()的函数。

  • 第一步:增加Module类,使用@Module声明类(可以将Module理解成生产对象的工厂,里面提供了provideXXX这种生产对象的方法)
@Module // Module注明该类是Module类
public class CoffeeModule {
    @Provides // Provides注明该方法是用来提供依赖对象的方法
    Heater provideHeater() {
        return new ElectricHeater();
    }
    @Provides
    Pump providePump() {
        return new Thermosiphon();
    }
}
  • 第二步:增加接口Component,使用@Component声明(Componet是将Module中生产的对象注入到对应的需要使用该对象的类中)
// 这里是声明要从CoffeeModule中去找对应的依赖,从`CoffeeModule`中去通过`provideXXX`方法来获取对应的对象
@Component(modules = CoffeeModule.class)
// 该接口会在编译时自动生成对应的实现类,这里是DaggerCoffeeComponent
public interface CoffeeComponent {
    // 提供一个供目标类使用的注入方法,该方法表示要将Module中的管理类注入到哪个类中,这里当然是CoffeeMaker,因为我们要用他俩去生产咖啡
    void inject(CoffeeMaker maker);
}

注意:@Component(modules = {AModule.class,BModule.class})可以设置多个Module,而且也可以@Component(modules = {MainModule.class}, dependencies = AppConponent.class) 指定依赖的Module和父Component

  • 第三步:Inject注入
import javax.inject.Inject;

class CoffeeMaker {
    @Inject // 标记该对象是被注入的
    Heater heater;
    @Inject
    Pump pump;


    CoffeeMaker() {
    	// DaggerCoffeeComponent这个类会在编译时产生,所以可以build一下
        CoffeeComponent component = DaggerCoffeeComponent.create();
        // 注入
        component.inject(this);

        // 或者使用下面的代码也是可以的
        //        DaggerCoffeeComponent.builder()
		//                .coffeeModule(new CoffeeModule())
		//                .build()
		//                .inject(this);
    }

    public void brew() {
        heater.on();
        pump.pump();
        System.out.println(" [_]P coffee! [_]P ");
        heater.off();
    }
}

到这里就完成了,执行的话,结果是一样的:

04-20 15:16:55.083 16375-16375/com.charon.stplayer I/System.out: ~ ~ ~ heating ~ ~ ~
04-20 15:16:55.084 16375-16375/com.charon.stplayer I/System.out: => => pumping => =>
     [_]P coffee! [_]P 

到这里你肯定就迷糊了,为什么? 本来很简单的东西,你还多搞出来几个类,弄的这么麻烦。但是麻烦吗? 你要是有十个地方呢? 一百个地方呢? 以后你修改怎么改? 一百个地方都改吗?

好了,最后来个总结,高中老师讲的,要来个综上所述:

  • 通过@Module声明一个XXXModule类,用于管理所有依赖,并使用@Provides为每个依赖提供provideXXX()方法
  • 通过@Component声明一个XXXComponent接口,并且声明要从哪些Module中寻找依赖
    • 声明要去哪些Module中寻找依赖 @Component(modules = CoffeeModule.class)
    • 提供一个供目标类使用的注入方法
      void inject(CoffeeMaker maker);
    • 为所有的依赖添加一个方法 这个说起来就有点多了,而且我们上面的代码中并没有进行这一步操作,这是因为方法也可以不写,但是如果要写,就按照这个格式来 但是当Component要被别的Component依赖时,这里就必须写这个方法,不写代表不向别的Component暴露此依赖,而且如果要写的话 这些方法返回值必须是从上面指定的依赖库CoffeeModule.class中取得的对象,注意:而方法名不一致也行,但是方便阅读,建议一致,因为它主要是根据返回值类型来找依赖的。 这里如果要写的话就是
      Heater provideHeater();
      Pump providePump();
  • 目标类使用依赖注入,首先是使用@Inject声明属性变量,表示注入这个依赖,然后是调用inject()方法注入依赖。

下面来个图,能够更方便的去理解:

image
上图中:

  • Container就是可以被注入的容器,具体对应上文例子中的CoffeeMakerContainer拥有需要被初始化的元素。需要被初始化的元素必须标上@Inject,只有被标上@Inject的元素才会被自动初始化。@InjectDagger2中一般标记构造方法与成员变量。
  • Module可以说就是依赖的原材料的制造工厂,所有需要被注入的元素的实现都是从Module生产的。
  • 有了可以被注入的容器Container,也有了提供依赖对象的Module。我们必须将依赖对象注入到容器中,这个过程由Component来执行。ComponentModule中产生的依赖对象自动注入到Container中。

好像大体明白了点....

上面有DaggerBaseComponent.create();DaggerBaseComponent.builder().build()两种方式有什么区别呢? 我们看一下源码:

  public static Builder builder() {
    return new Builder();
  }

  public static BaseComponent create() {
    return new Builder().build();
  }

Dagger2会自动生成该接口的实现类(以Dagger开头,如果@Component注解类不是顶级类,自动生成的实现类的名字会闭包命名并用下划线分割如DaggerFoo_Bar_BazComponent),可以通过该实现类的builder()方法获得builder对象,builder对象可以设置好依赖,最后通过build()方法构建实例:

CoffeeShop coffeeShop = DaggerCoffeeShop.builder()
    .dripCoffeeModule(new DripCoffeeModule())
    .build();

如果Modules都是缺省构造器,@Provides方法都是静态的,用户不需要构建依赖实例,那么自动生成的实现类就会有create()方法获取新实例,就不需要处理builder了。

public class CoffeeApp {
  public static void main(String[] args) {
    CoffeeShop coffeeShop = DaggerCoffeeShop.create();
    coffeeShop.maker().brew();
  }
}

下一篇:3.Dagger2入门demo扩展(三)