preface

The big guys from the former architecture group shared a piece of content: How to make App Bundle support Dagger2.

PS: The App Bundle is not the subject of this article

During the meeting, there was a heated discussion on how to use Dagger2 efficiently in App Bundle. XXX said that the team should strengthen technical construction and standardize the use of Dagger2…

I’ve never used a Fucking Dagger2. Who am I? Where I am? What am I listening to?

The body of the

Why dependency injection

Personally, start learning a new skill. The premise is to figure out what kind of problems, pain points this technology will solve for us. Dagger, what does it solve?

For us, as the project gets bigger and bigger, the class explodes and the code volume explodes. If some of our classes have direct dependencies on some of our classes, we will find a lot of new, resulting in extremely severe coupling between our classes, which in turn makes our maintenance more complex.

At this point, the idea of dependency injection is gradually put on the statute. Use injection instead of internal new. Dagger is one of the dependency injection frameworks. Due to its compile time generating code (no performance loss), the transparent dependency code becomes popular quickly

Okay, let’s go. Here we go.

2. Basic Usage

2.1. Introductory Notes

  • @Module and @Provides: Defines the classes and methods that provide dependencies
  • Inject: Required dependency. Can be used on constructors, fields, or methods
  • @Component: Used to build the interface that wires all dependencies together

2.2. Getting started demo

Before Dagger2 starts, we want to use a normal demo. Background from Game of Thrones, code source: Dagger 2 for Android Beginners — Dagger 2 Part1

PS: Access to this site requires scientific Internet access…

public class BattleOfBastards {

    public static void main(String[] args){
        // IronBank, Allies, Starks, And Boltons are just general classes.
        IronBank bank = new IronBank();
        Allies allies = new Allies(bank);
        Starks starks = new Starks(allies, bank);
        Boltons boltons = new Boltons(allies, bank);

        War war = newWar(starks, boltons); war.prepare(); war.report(); }}Copy the code

As you can see, in order to run this demo, we need to new out a lot of the classes that we need. As you can see, this class becomes more difficult to maintain as the project grows.

Let’s take a look at the transformation with Dagger2:

public class Starks implements House {
    @Inject
    public Starks(a){}

    @Override
    public void prepareForWar(a) {
        System.out.println(this.getClass().getSimpleName()+" prepared for war");
    }

    @Override
    public void reportForWar(a) {
        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(a){
        starks.prepareForWar();
        boltons.prepareForWar();
    }

    public void report(a){ starks.reportForWar(); boltons.reportForWar(); }}@Component
interface BattleComponent {
    War getWar(a);
}
Copy the code

BattleComponent: BattleComponent: BattleComponent: BattleComponent: BattleComponent: BattleComponent: BattleComponent

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

Obviously BattleComponent takes tons of damage for us. So let’s take a look at BattleComponent source code:

public final class DaggerBattleComponent implements BattleComponent {
  private DaggerBattleComponent(Builder builder) {}

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

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

  @Override
  public War getWar(a) {
    return new War(new Starks(), new Boltons());
  }

  public static final class Builder {
    private Builder(a) {}

    public BattleComponent build(a) {
      return new DaggerBattleComponent(this); }}}Copy the code

Here we can see that it implements the BattleComponent interface, and getWar() provides an instance of War by means of new War(), because we Inject the constructor of War, The same goes for new Starks(), new Boltons().

2.3. @Module and @provide annotations

Here we have a getWar() method in @Component because we need to get an instance of War. If I want to getStarks externally, can I define getStarks() as well? Why do you have to ask? Of course!

@Component
interface BattleComponent {
    War getWar(a);
    Starks getStarks(a);
    Boltons getBoltons(a);
}
Copy the code

The @Inject in Starks and Boltons remains unchanged. Now let’s rebuild and have a look at the DaggerBattleComponent:

public final class DaggerBattleComponent implements BattleComponent {
  // omit the same content
  @Override
  public War getWar(a) {
    return new War(getStarks(), getBoltons());
  }

  @Override
  public Starks getStarks(a) {
    return new Starks();
  }

  @Override
  public Boltons getBoltons(a) {
    return new Boltons();
  }
  // omit the same content
}
Copy the code

GetStarks () is implemented automatically using new Starks(); New because we annotate @inject into the Starks constructor.

What happens if we don’t have @inject here?

Don’t try, we’ll eventually be prompted to provide @inject or @provides interfaces. Now that we have @Provides, let’s take a look at it, but first we need to take a look at @Module:

@Module

The @module annotation marks a Module/class. For example, in Android. We can have a calling module ContextModule that provides ApplicationContext and Context dependencies for other classes. Therefore, we need to tag ContextModule with @Module.

Don’t understand, it doesn’t matter. And we’ll come back to that in a moment with the code.

@Provides

The @provides annotation marks methods inside a Module and Provides a way for the outside world to get dependencies. For example, in the Android example mentioned above, where the ContextModule uses the @Module flag, we need to use the @provides flag to provide the ApplicationContext and Context instance methods.

Don’t understand, it doesn’t matter. And we’ll come back to that in a moment with the code.

Now let’s add these two notes to BattleOfBastards as well:

We’ll introduce two new characters: money and soldiers

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

public class Soldiers {
    public Soldiers(a){  //do something }
}
Copy the code

Then we construct a Module to manage our money and soldiers:

@Module
public class BraavosModule {
    Cash cash;
    Soldiers soldiers;

    public BraavosModule(Cash cash, Soldiers soldiers){
        this.cash=cash;
        this.soldiers=soldiers;
    }

    @Provides
    Cash provideCash(a){
        return cash;
    }

    @Provides
    Soldiers provideSoldiers(a){
        returnsoldiers; }}Copy the code

Here @Module adds more dependencies for us. How do we make it work? In this way:

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

And then we can use our Cash and Soldiers like that.

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

Our component. GetCash (); The resulting Cash instance is provided by provideCash() in the BraavosModule instance. That’s what we annotate at sign. But how does @Provides generate the code? Let’s take a look:

public final class DaggerBattleComponent implements BattleComponent {
    // Omit some code

    @Override
    public Cash getCash(a) {
        returnBraavosModule_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;
  }

  // omit some useless content
  public static Cash proxyProvideCash(BraavosModule instance) {
    return Preconditions.checkNotNull(
        instance.provideCash(), "Cannot return null from a non-@Nullable @Provides method"); }}Copy the code

As you can see from the generated code above, when we call getCash(), we actually call the proxyProvideCash(BraavosModule instance) of the BraavosModule_ProvideCashFactory instance, This method finally calls instance.providecash () to return what we @provide.

The end of the

I did not expect to write the basic content, has involved so much content. If so, I’ll stop here, but I’ll save the Android article for the next one.

I am a fresh graduate, recently and friends maintain a public account, the content is that we in the transition from fresh graduate to the development of this way stepped on the pit, as well as our step by step learning records, if interested in friends can pay attention to it, together with fuel ~