美文网首页
Dagger2 依赖的接力游戏(三):Module的使用和原理

Dagger2 依赖的接力游戏(三):Module的使用和原理

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

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

    PS: 本篇文章的示例代码,放在了项目的chapter3分支

    上一篇文章我们介绍了使用@inject声明提供类型,并据此实现了Car和Engine的完全依赖注入,这篇文章我们使用Module来实现同样的效果。

    我们先梳理一下上篇文章里的依赖关系,我们最后总结了它的依赖模型是这样的:

    D(Car,Engine) = D(Car,DaggerCarComponent)
    

    加上Dagger2托管的依赖关系实际上是这样的:

    D(Car,Engine) = D(Car,DaggerCarComponent) + D(DaggerCarComponent,Car构造方法) + D(Car构造方法, Engine构造方法)
    

    我们先调整Engine的提供,使用Module来实现。

    定义EngineModule

    首先我们聊一下什么是Module,Module是Dagger2为我们定义的一个角色,它是一个“提供类型P(?)”的容器,我们用@Module注解一个类来声明这个类是一个module,在这个类里面,所有用@Provides注解的返回类型不为空的方法都是合法的提供类型。要注意的是,Dagger2框架不为Null对象做任何的错误兼容处理,相反地,它会在注入前检查Module创建的对象是否为空,如果为空就报空指针错误,所以我们要确保Module的提供方法返回一个可用的对象。

    如果我们想返回可能为空的对象,可以使用@Nullable注解声明,这个注解是类型递归的,使用了这个注解的方法,Component里也必须同样声明,否则会编译失败。

    现在我们创建一个EngineModule来实现P(Engine)。

    @Module
    public class EngineModule {
    
        @Provides
        Engine provideEngine(){
            return new Engine(2);
        }
    
    }
    

    声明对EngineModule的依赖

    创建后我们要告诉CarComponent使用EngineModule来作为它的提供类型的容器,查找它需要的提供类型。Component注解定义了modules索引的class数组参数,用来声明它依赖的所有Module。

    这里和使用Inject注解构造方法不同之处在于,类的构造方法总是唯一的(参数类型唯一),因此它作为“提供类型”也是唯一的,所以Dagger2会自动将构造方法声明的提供类型,作为所有Component的备用类型,不用我们手动告诉具体的Component。但是如果在Module里声明提供类型,我们可以在多个Module里声明相同的提供类型,根据不同的使用需求对Component进行装配,因此多了告诉Component它的Modules依赖这一步,这一步也是我们使用Dagger2来管理依赖关系的优越性的体现:我们只要修改注解,就可以修改具体的依赖关系。

    我们修改一下CarComponent接口:

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

    现在我们使用Module声明了Engine的提供类型,main方法的调用方式保持不变:

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

    我们运行一下:

    cylinderNumbers : 2
    
    Process finished with exit code 0
    

    Engine的气缸数目已经变为了2,也就是说我们修改生效了。

    Module的工作机制

    现在我们再看看生成的注入代码,了解一下Module的工作机制。我们先看下getCar方法:

      @Override
      public Car getCar() {
        return injectCar(
            Car_Factory.newCar(EngineModule_ProvideEngineFactory.proxyProvideEngine(engineModule)));
      }
    

    对比使用@Inject声明的模板代码,我们发现前半部分

     injectCar(Car_Factory.newCar(xxx));
    

    是一样的,不同的是前者直接创建了一个Engine对象,而这里使用了模板来获取对象,Dagger为我们生成了与Module名称和方法名称对应关系为:

    模块名_方法名Factory.proxy方法名(模块名 instance)
    

    的静态类和方法,接受EngineModule对象,并作为它的代理来获取对象,我们看看代理方法:

      public static Engine proxyProvideEngine(EngineModule instance) {
        return Preconditions.checkNotNull(
            instance.provideEngine(), "Cannot return null from a non-@Nullable @Provides method");
      }
    

    这里调用了Module实例中我们自己定义的provideEngine方法,并校验module生成的对象是否为空。我们再看看这个EngineModule实例是怎么初始化的:

        public CarComponent build() {
          if (engineModule == null) {
            this.engineModule = new EngineModule();
          }
          return new DaggerCarComponent(this);
        }
    

    在使用Builder构建CarComponent对象的时候,如果为空就创建一个新的实例,并传递给Component对象。同时我们也看到,在Builder中生成了指定module的方法:

        public Builder engineModule(EngineModule engineModule) {
          this.engineModule = Preconditions.checkNotNull(engineModule);
          return this;
        }
    

    所以我们可以总结一下,如果我们为AComponent实现XModule的YMethod方法,来声明一个P<Type>,Dagger为我们做了以下的工作来完成这个功能。

    1. 生成XModule_YMethodFactory.proxyYMethod(XModule instance)的静态代理类和方法,并作为Type实例的获取代理
    2. 在AComponent和AComponent.Builder中生成XModule的成员,并为XModule成员生成建造模式的初始化方法。
    3. 在getType()方法里调用第1步生成的代理方法,获取Type实例作为依赖,创建我们要的目标实例。

    这个环节完成的依赖模型替换为:

    D(DaggerCarComponent,Engine) = D(DaggerCarComponent,EngineModule_ProvideEngineFactory) + D(EngineModule_ProvideEngineFactory, EngineModule) + D(EngineModule,Engine)
    
    

    这里值得注意的有以下几点:

    1. 之所以在Builder里生成XModule成员的建造模式方法,是为了提供机制和能力,让我们更加灵活地管理具体的Module,控制对象实例化逻辑。
    2. D(EngineModule,Engine) 是我们手动实现的,是我们真正的业务逻辑,需要我们多加关注,保证逻辑符合预期。

    小结

    在这一节中,我们使用module来实现了Engine的提供,并通过源码和逻辑模型,分析了module的工作机制以及其背后的设计原理。但是我们看到,作为Car类的组件,依赖了Engine的Module,稍微有点别扭。有时候我们想控制Component、Module的对应关系,比如为了使我们的代码逻辑更加清晰可读,将这种复用的需求,通过组件的复用来实现。下一篇文章,我们将讲解如何复用Component,敬请期待。

    相关文章

      网友评论

          本文标题:Dagger2 依赖的接力游戏(三):Module的使用和原理

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