美文网首页
Dagger2 依赖的接力游戏(四):Component的复用

Dagger2 依赖的接力游戏(四):Component的复用

作者: 散落_a0b3 | 来源:发表于2019-01-02 22:45 被阅读0次

    文接Dagger2 依赖的接力游戏(三)

    上一篇文章我们使用CarComponent依赖EngineModule讲解了Module的使用和原理,并引出了Component的复用问题。现在我们通过CarComponent复用EngineComponent来实现Engine的注入。

    Component复用有两种形式,一种是使用dependencies参数声明它的依赖组件,一个是用subcomponent声明它的子组件,这两个参数接受的都是class数组。Dagger2要求我们手动声明Component之间的依赖关系的原因,和上面说的声明Module依赖的原因是一样的,就是为了提供机制,让我们可以灵活地调整我们的依赖关系。第一种比较简单直观,我们先用第一种来实现。

    使用组件依赖实现注入

    PS: 本节的示例代码收录在项目的chapter4.1分支

    我们修改EngineComponent,让它依赖EngineModule:

    @Component(modules = EngineModule.class)
    public interface EngineComponent {
    
        Engine getEngine();
    
    }
    

    然后我们再修改CarComponent的依赖:

    @Component(dependencies = EngineComponent.class)
    public interface CarComponent {
    
        void inject(Car car);
    
        Car getCar();
    }
    

    main的方法也保持不变,我们这里也贴一下便于理解:

    public class Main {
        public static void main(String[] args){
            Car car = DaggerCarComponent.builder().build().getCar();
            System.out.println("cylinderNumbers : " + car.getEngine().getCylinderNumbers());
        }
    }
    

    我们运行一下,发现报错了:

    Exception in thread "main" java.lang.IllegalStateException: com.example.dagger2.EngineComponent must be set
        at com.example.dagger2.DaggerCarComponent$Builder.build(DaggerCarComponent.java:52)
        at com.example.dagger2.Main.main(Main.java:12)
    

    我们查看一下报错的代码:

    public final class DaggerCarComponent implements CarComponent {
    
      //...此处省略N行代码
      public static final class Builder {
      //...此处省略N行代码
        public Builder engineComponent(EngineComponent engineComponent) {
          //...此处报错了
          this.engineComponent = Preconditions.checkNotNull(engineComponent);
          return this;
        }
      }
    }
    

    如果我们稍微跟踪一下代码逻辑,会发现DaggerCarComponent.Builder没有为我们创建EngineCarComponent对象,而是直接检查这个对象是否未空,所以我们要在main方法里手动创建,并传递给它,我们修改一下main方法:

    public class Main {
        public static void main(String[] args){
            EngineComponent engineComponent = DaggerEngineComponent.create();
            Car car = DaggerCarComponent.builder().engineComponent(engineComponent).build().getCar();
            System.out.println("cylinderNumbers : " + car.getEngine().getCylinderNumbers());
        }
    }
    

    我们运行一下:

    cylinderNumbers : 2
    
    Process finished with exit code 0
    

    结果是正确的了。从这里我们可以看出来如果使用组件依赖的方式来满足需求,实际上使用的还是两个独立的组件,只不过依赖组件需要被依赖组件的实例才能正常工作。Dagger2会为被依赖的组件创建完整的注入模板,因此也会在编译时检查被依赖组件依赖关系的完整性。被依赖组件的实例需要我们自己维护,依赖组件只负责使用创建好的被依赖组件的实例。我们再使用子组件来实现EngineComponent的依赖。

    使用子组件实现依赖注入

    PS: 本节的示例代码收录在项目的chapter4.2分支

    在示例开始之前,我们要讲一下什么是子组件,讲清楚这个概念是理解我们示例的关键。我自己在学习的时候,就子组件这里卡了很久才慢慢摸清楚到底要怎么用它,dagger对它到底有哪些支持和限制。所以这个部分非常重要,请慢下来仔细看。

    我们在第二篇中解释了,组件是依赖关系的容器。在上一小结的组件依赖示例中,我们演示了CarComponent依赖EngineComponent的工作模式,如果我们用集合C(collection)表示它们的容器情况,是这样的:

    C(EngineComponent) = { P(Engine) }
    
    C(CarComponent) = {P(Car), P(Engine), I(Car,Engine)}
    

    C(EngineComponent) 和C(CarComponent)都是完整的依赖集合,其中C(CarComponent)中P(Engine)是通过组件依赖,由EngineModule提供的,也就是说EngineComponent作为CarComponent的一部分,才满足了CarComponent的依赖完整性,只是在生成的注入模板之后,需要我们独立维护实例。

    而我一开始看到子组件的时候,以为子组件就是这么回事!!!

    实际上子组件的定义刚好和这个相反,如果我们把EngineComponent定义成CarComponent的子组件,容器情况是这样的:

    C(CarComponent) = {P(Car), (Car,Engine)}
    
    C(EngineComponent) = {P(Car), I(Car,Engine),  P(Engine)}
    

    看懂了吗?子组件继承了父组件里所有的依赖关系,父组件才是它的一个子集。父组件的依赖关系是完整的,而子组件在定义的时候依赖关系可以是不完整的,在绑定到父组件的时候再补齐依赖关系。但是我们在使用的时候,要通过父组件里的接口暴露子组件来使用的。这样的一个依赖和使用的反差,带来的好处有两个:

    1. 父组件无法使用子组件的依赖关系,子组件对自己的依赖关系可以起到封装和保护的作用。
    2. 父组件是依赖完整的,可以独立于子组件存在,因此子组件可以有独立的生命周期,这也是子组件设计的目的。

    了解了这个基础之后,我再看看依赖关系。Car类依赖Engine类,Engine类是可以独立存在的。因此我们在设计的时候应该调整父子组件的关系,CarComponent是子组件,EngineComponent是父组件。为了完整演示一个子组件,我们用CarModule来实现P(Car):

    @Module
    public class CarModule {
        @Provides
        Car provideCar(Engine engine){
            return new Car(engine);
        }
    }
    

    我们再看看子组件怎么声明:

    //子组件声明注解,参数及作用和组件一模一样
    @Subcomponent(modules = CarModule.class)
    public interface CarComponent {
    
        Car getCar();
    
      //子组件必须要有一个Buidler声明,否则父组件不知道怎么构建子组件
        @Subcomponent.Builder
        interface Builder{
    
            //可选的方法,不过如果不声明,就没法指定子组件的Module
            Builder carModule(CarModule module);
            
            //创建子组件的方法,必须声明
            CarComponent build();
    
        }
    }
    

    可以看到我们子组件的依赖是不完整的,缺少了P(Engine)。

    子组件要绑定到父组件,要通过module来实现:

    //将子组件绑定到module
    @Module(subcomponents = CarComponent.class)
    public class EngineModule {
    
        @Provides
        Engine provideEngine(){
            return new Engine(2);
        }
    
    }
    
    

    接下来我们要在父组件里暴露子组件的Builder,否则虽然依赖关系是满足了,但是无法使用:

    @Component(modules = EngineModule.class)
    public interface EngineComponent {
    
        Engine getEngine();
    
        //通过Builder暴露子组件
        CarComponent.Builder carComponent();
    
    }
    

    然后我们在main方法里修改一下调用方式:

    public class Main {
        public static void main(String[] args){
            
            Car car = DaggerEngineComponent.builder()
                    .engineModule(new EngineModule())
                    .build()
                    .carComponent()
                    .carModule(new CarModule())
                    .build().getCar();
    
            System.out.println("cylinderNumbers : " + car.getEngine().getCylinderNumbers());
        }
    }
    

    运行一下结果是正确的。

    cylinderNumbers : 2
    
    Process finished with exit code 0
    

    子组件的几个定义很容易写懵逼,没有仔细琢磨过简直不理解为什么dagger要我们手动写这么多东西。为了方便理解,我把注意点全部写在了注释中,紧跟代码,方便对照。这个小节强烈建议大家实践一下,然后回头再看前面子组件的定义,才能更好地理解依赖的继承关系。

    通过调用方式我们可以看到,子组件是依赖于父组件才能进行工作的,它并不会被独立的编译成注入代码,而是通过内部类的方式来实现子组件接口,外部是无法实例化子组件的对象的,通过这种方式,来保证子组件对父组件的依赖性。

    小结

    本篇我们使用组件复用的方式,实现了Engine的依赖注入。然后通过逻辑模型,分析了其背后的复用逻辑。源码分析这里就不在做了,读者可以跟自己查看一下源码,结合设计目的,理解一下为什么这么实现。截止目前为止,我们都是采用不同的方式,实现相同的Engine注入效果。这是最简单的依赖关系,下一篇开始,我们开始讲解复杂的依赖需求,包括使用相同返回类型相同,但是实例需求不同,包括实例的作用域控制等等。敬请期待。

    参考文档

    知乎: 神兵利器dagger2
    github : Dagger2官方入口
    Dagger 2 for Android Beginners
    Dagger2入门解析

    相关文章

      网友评论

          本文标题:Dagger2 依赖的接力游戏(四):Component的复用

          本文链接:https://www.haomeiwen.com/subject/qbstrqtx.html