美文网首页Android开发Android开发Android开发经验谈
出来混迟早要还的,技术债Dagger2:基础篇

出来混迟早要还的,技术债Dagger2:基础篇

作者: 咸鱼正翻身 | 来源:发表于2019-02-10 23:34 被阅读19次

    前言

    年前架构组的大佬们,分享了一个内容:如何让App Bundle支持Dagger2。

    PS:关于App Bundle暂时不是本篇内容要讲的

    会议就如何在App Bundle中高效的使用Dagger2展开了激烈的讨论,xxx表示应加强团队技术建设,规范Dagger2的使用...

    我tm都没用过Dagger2,我是谁?我在哪?我都在听些什么?

    正文

    一、为什么需要依赖注入

    个人觉得,开始一个新技术的学习。前提是弄清楚这个技术会为我们解决什么样的问题、痛点。
    拿Dagger来说,它解决了什么呢?

    对于我们来说,随着项目的越来越大,类爆炸,代码量激增的情况便会显现出来。如果我们某些类与某些类直接存在依赖关系的话,我们就会发现大量的new,导致了我们的类与类之间耦合的极为严重,进而我们的维护也变得更加复杂。

    此时,依赖注入的思想便逐步被提上章程。用注入的方式,替代原本需要内部new的方式。而Dagger就是依赖注入框架之一,因其编译期生成代码(无性能损失),依赖关系代码透明的特点迅速走红~~

    OK,不扯别的了,开始。

    二、基础用法

    2.1、入门注解

    • @Module和@Provides:定义提供依赖关系的类和方法
    • @Inject:需要的依赖。可以在构造函数,字段或方法上使用
    • @Component:用于构建将所有依赖关系连接在一起的接口

    2.2、入门demo

    Dagger2开始前,我们想用一个普通的demo。背景来自于《权利的游戏》,代码来源:Dagger 2 for Android Beginners — Dagger 2 part1

    PS:访问此网站需要科学上网...

    public class BattleOfBastards {
    
        public static void main(String[] args){
            // 关于IronBank、Allies、Starks、Boltons因为篇幅原因就不展开了,就是普通的类而已。
            IronBank bank = new IronBank();
            Allies allies = new Allies(bank);
            Starks starks = new Starks(allies, bank);
            Boltons boltons = new Boltons(allies, bank);
    
            War war = new War(starks, boltons);
            war.prepare();
            war.report();
        }
    }
    

    我们可以看到,想要运行这个demo,我们需要new出很多需要的类。可见当项目逐渐增大,这个类的维护难度可想而知。

    接下来我们看一下用Dagger2进行改造:

    public class Starks implements House {
        @Inject
        public Starks(){}
    
        @Override
        public void prepareForWar() {
            System.out.println(this.getClass().getSimpleName()+" prepared for war");
        }
    
        @Override
        public void reportForWar() {
            System.out.println(this.getClass().getSimpleName()+" reporting..");
        }
    }
    // Boltons类同上
    
    public class War {
        private Starks starks;
        private Boltons boltons;
    
        @Inject
        public War(Starks starks, Boltons bolton){
            this.starks = starks;
            this.boltons = bolton;
        }
    
        public void prepare(){
            starks.prepareForWar();
            boltons.prepareForWar();
        }
    
        public void report(){
            starks.reportForWar();
            boltons.reportForWar();
        }
    }
    
    @Component
    interface BattleComponent {
        War getWar();
    }
    

    此时我们需要build一下,我们需要通过编译,让Dagger2帮我们生成我们所需要的类:BattleComponent,编译成功,我们就可以改造main()啦。

    public class BattleOfBastards {
        public static void main(String[] args){
            BattleComponent component = DaggerBattleComponent.create();
            War war = component.getWar();
            war.prepare();
            war.report();
        }
    }
    

    很明显能够看出来,BattleComponent为我们承受了成吨的伤害。那么就让我们看一看BattleComponent的源码:

    public final class DaggerBattleComponent implements BattleComponent {
      private DaggerBattleComponent(Builder builder) {}
    
      public static Builder builder() {
        return new Builder();
      }
    
      public static BattleComponent create() {
        return new Builder().build();
      }
    
      @Override
      public War getWar() {
        return new War(new Starks(), new Boltons());
      }
    
      public static final class Builder {
        private Builder() {}
    
        public BattleComponent build() {
          return new DaggerBattleComponent(this);
        }
      }
    }
    

    这里我们可以看到,它实现了BattleComponent接口,并且getWar()是通过new War()的方式提供War的实例,这是因为,我们@Inject了War的构造方法,new Starks(), new Boltons()也是同样的道理。

    2.3、@Module和@Provide注释

    这里我们因为需要获取War的实例,在@Component了一个getWar()方法。那一定会有朋友问,如果我想外部获取Starks,是不是也可以定义一个getStarks()?那还用问吗,当然可以!

    @Component
    interface BattleComponent {
        War getWar();
        Starks getStarks();
        Boltons getBoltons();
    }
    

    StarksBoltons中的@Inject不变。这时我们重新Build一下,再看一看DaggerBattleComponent:

    public final class DaggerBattleComponent implements BattleComponent {
      // 省略相同内容
      @Override
      public War getWar() {
        return new War(getStarks(), getBoltons());
      }
    
      @Override
      public Starks getStarks() {
        return new Starks();
      }
    
      @Override
      public Boltons getBoltons() {
        return new Boltons();
      }
      // 省略相同内容
    }
    

    这里自动实现的getStarks()是采用了new Starks();的方式,为什么是new,是因为我们@Inject注解到了Starks的构造方法上了。

    如果这里我们没有@Inject会发生什么呢?

    不用试了,最终会提示我们:需要提供@Inject或@Provides接口。既然提到了@Provides,那就让我们看一看它,不过说它之前,我们需要先看一看@Module:

    @Module

    @Module注释标记模块/类。例如,在Android中。我们可以有一个调用的模块ContextModule,该模块可以为其他类提供ApplicationContext和Context依赖。因此,我们需要将类标记ContextModule用@Module。

    不理解,不要紧。一会结合代码再回过头就明白了。

    @Provides

    @Provides注释标记Module内部的方法,为外部提供了获取依赖的方式。例如,在上面提到的Android示例中,我们ContextModule使用@Module标记,我们需要使用@Provides标记提供ApplicationContext和Context实例的方法。

    不理解,不要紧。一会结合代码再回过头就明白了。

    接下来让我们把这俩个注解的内容也加入到BattleOfBastards之中:

    先引入俩个全新的角色:钱和士兵

    public class Cash {
        public Cash(){    //do something    }
    }
    
    public class Soldiers {
        public Soldiers(){  //do something    }
    }
    

    然后我们构造一个Module,用它来管理我们的钱和士兵:

    @Module
    public class BraavosModule {
        Cash cash;
        Soldiers soldiers;
    
        public BraavosModule(Cash cash, Soldiers soldiers){
            this.cash=cash;
            this.soldiers=soldiers;
        }
    
        @Provides
        Cash provideCash(){
            return cash;
        }
    
        @Provides
        Soldiers provideSoldiers(){
            return soldiers;
        }
    }
    

    这里@Module为我们增加了更多的依赖性,怎么让它生效呢?这样既可:

    @Component(modules = BraavosModule.class)
    interface BattleComponent {
        War getWar();
        
        Cash getCash();
        Soldiers getSoldiers();
    }
    

    然后我们可以这样使用我们的CashSoldiers

    public class BattleOfBastards {
        public static void main(String[] args){
            BattleComponent component = DaggerBattleComponent
                    .builder().braavosModule(new BraavosModule(new Cash(), new Soldiers())).build();
            War war = component.getWar();
            war.prepare();
            war.report();
    
            component.getCash();
            component.getSoldiers();
        }
    }
    

    我们component.getCash();所得到的Cash实例,是BraavosModule实例中provideCash()所提供的。也就是我们@Provides也注解的内容。不过@Provides是如何生成代码的?让我们一同看一下:

    public final class DaggerBattleComponent implements BattleComponent {
        // 省略部分代码
    
        @Override
        public Cash getCash() {
            return BraavosModule_ProvideCashFactory.proxyProvideCash(braavosModule);
        }
    }
    
    // BraavosModule_ProvideCashFactory
    public final class BraavosModule_ProvideCashFactory implements Factory<Cash> {
      private final BraavosModule module;
    
      public BraavosModule_ProvideCashFactory(BraavosModule module) {
        this.module = module;
      }
    
      // 省略部分无用内容
      public static Cash proxyProvideCash(BraavosModule instance) {
        return Preconditions.checkNotNull(
            instance.provideCash(), "Cannot return null from a non-@Nullable @Provides method");
      }
    }
    

    从上述生成的代码可以看到,我们调用getCash()时,实际上是调用了BraavosModule_ProvideCashFactory实例的proxyProvideCash(BraavosModule instance),这个方法最终调用instance.provideCash()返回我们@Provide的内容。

    尾声

    没想到光写基础的内容,就已经涉及到了这么多的内容。既然如果那么本篇内容就先止步于此,关于Android篇的内容,就放在下一篇文章中吧!

    我是一个应届生,最近和朋友们维护了一个公众号,内容是我们在从应届生过渡到开发这一路所踩过的坑,以及我们一步步学习的记录,如果感兴趣的朋友可以关注一下,一同加油~

    个人公众号:咸鱼正翻身

    相关文章

      网友评论

        本文标题:出来混迟早要还的,技术债Dagger2:基础篇

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