Lazy loading, Lazy

Sometimes, some objects need to be loaded lazily and not created at the beginning to reduce memory overhead and initialization time. So, for any type T, you can create an object of type Lazy that returns an instance of type T the first time the get() method is called. If T is a singleton, then all injections return the same Lazy instance in scope. Otherwise, each injection returns the respective Lazy instance.

BMWWheel:

public class BMWWheel implements IWheel {
    @Inject
    public BMWWheel(a) {
        super(a); }/ /...
}
Copy the code

WheelModule:

@Module
abstract class WheelModel {
    @Binds
    abstract IWheel provideBMWWheel(BMWWheel wheel);
}
Copy the code

CarComponent:

@Component(modules = WheelModel.class)
public interface CarComponent {
    BMW makeBmw(a);
}
Copy the code

Testing:

public class CarMain {
    public static void main(String[] args) { CarComponent component = DaggerCarComponent.create(); BMW bmw = component.makeBmw(); System.out.println(bmw.wheel); System.out.println(bmw.wheel); System.out.println(bmw.wheel); System.out.println(bmw.wheel); }}Copy the code

The result is that the wheel is the same object each time it is printed.

Implementation principle:

DaggerCarComponent:

public final class DaggerCarComponent implements CarComponent {
  private DaggerCarComponent(a) {}public static Builder builder(a) {
    return new Builder();
  }

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

  @Override
  public BMW makeBmw(a) {
    returninjectBMW(BMW_Factory.newInstance()); }private BMW injectBMW(BMW instance) {
    BMW_MembersInjector.injectWheel(instance, DoubleCheck.lazy((Provider) BMWWheel_Factory.create()));
    return instance;
  }

  public static final class Builder {
    private Builder(a) {}public CarComponent build(a) {
      return newDaggerCarComponent(); }}}Copy the code

As you can see, the Wheel property injected into the BMW object instance by the DaggerCarComponent is of type DoubleCheck. While DoubleCheck internally implements the get() method and uses synchronized to ensure that the same object is acquired every time.

  @Override
  public T get(a) {
    Object result = instance;
    if (result == UNINITIALIZED) {
      synchronized (this) {
        result = instance;
        if (result == UNINITIALIZED) {
          result = provider.get();
          instance = reentrantCheck(instance, result);
          /* Null out the reference to the provider. We are never going to need it again, so we * can make it eligible for GC. */
          provider = null; }}}return (T) result;
  }
Copy the code

The e reentrantCheck() method is a bit confusing.

Another test:

public class CarMain {
    public static void main(String[] args) { CarComponent component = DaggerCarComponent.create(); System.out.println(component.makeBmw().wheel); System.out.println(component.makeBmw().wheel); System.out.println(component.makeBmw().wheel); }}Copy the code

Lazy injection is only associated with Lazy objects. If the Lazy object instances are different and the type T is not a singleton, a different object will be returned each time.

The Provider injection

If you want to get a different instance of type T each time, you can use Provider injection, which is Provider. This will re-invoke the binding logic in Module each time the get() method is called. If a new object instance is provided in each Module, the Provider’s get() method is the same instance every time it is called.

BMWWheel ditto

BMW:

public class BMW {
    @Inject
    public BMW(a){
        super(a); }@Inject
    Provider<IWheel> wheel;
}
Copy the code

The difference is that providers are injected instead of IWheel or Lazy

BMWModule and CarComponent do the same.

Testing:

public class CarMain {
    public static void main(String[] args) { CarComponent component = DaggerCarComponent.create(); BMW bmw = component.makeBmw(); System.out.println(bmw.wheel.get()); System.out.println(bmw.wheel.get()); System.out.println(bmw.wheel.get()); }}Copy the code

Obviously, each printed IWheel instance is different.

The main logic is in the DaggerCarComponent:

public final class DaggerCarComponent implements CarComponent {
  private DaggerCarComponent(a) {}/ /...
  private BMW injectBMW(BMW instance) {
    BMW_MembersInjector.injectWheel(instance, WheelModel_ProvideBMWWheelFactory.create());
    return instance;
  }
	/ /...
}
Copy the code

As you can see, injected into the BMW object is an instance of the factory class -WheelModel_ProvideBMWWheelFactory:

public final class WheelModel_ProvideBMWWheelFactory implements Factory<IWheel> {
  @Override
  public IWheel get(a) {
    return provideBMWWheel();
  }

  public static WheelModel_ProvideBMWWheelFactory create(a) {
    return InstanceHolder.INSTANCE;
  }

  public static IWheel provideBMWWheel(a) {
    return Preconditions.checkNotNull(WheelModel.provideBMWWheel(), "Cannot return null from a non-@Nullable @Provides method");
  }

  private static final class InstanceHolder {
    private static final WheelModel_ProvideBMWWheelFactory INSTANCE = newWheelModel_ProvideBMWWheelFactory(); }}Copy the code

As you can see, the WheelModel_ProvideBMWWheelFactory factory class is a singleton of a static inner class implementation. That is, the wheel variable of type Provider in different BMW instances actually refers to the same singleton.

It implements the get () method, internal will call provideBMWWheel () method, and this method will call again WheelModel. ProvideBMWWheel () method, so they tested before, Each call to the Provider’s get() method re-invokes the @Module class method that provides the dependency. Therefore, it is possible to imagine that a Provider cannot guarantee that every instance of type T retrieved will be different, depending on the implementation in the @Module, such as @Singleton.

BMW:(add @singleton to BMWWheel)

@Singleton
public class BMWWheel implements IWheel {
    @Inject
    public BMWWheel(a) {
        super(a); }/ /...
}
Copy the code

WheelModel:

@Module
abstract class WheelModel {
    @Binds
    abstract IWheel provideBMWWheel(BMWWheel wheel);
}
Copy the code

Add @singleton to the CarComponent

So it’s clear that each call to WheelModel’s provideBMWWheel() gets the same BMWWheel instance, because @Singleton is valid in the Component.