Skip to content

Latest commit

 

History

History
268 lines (210 loc) · 7.87 KB

5.Dagger2Lay和Provider(五).md

File metadata and controls

268 lines (210 loc) · 7.87 KB

Dagger2Lay和Provider(五)

Lazy<>(延迟加载)Provider<>(强制重新加载)

我们再回到之前的例子,现在我们把前面使用单例的部分都去掉,再来继续讲其他的部分。在上面的CoffeeModule中,把每个provideXXX()都添加一句log:

@Module
public class CoffeeModule {
    @Provides
    Heater provideHeater() {
        System.out.println("provide heater");
        return new ElectricHeater();
    }

    @Provides
    Pump providePump() {
        System.out.println("provide pump");
        return new Thermosiphon();
    }

    @Provides
    Ice provideIce() {
        System.out.println("provide ice");
        return new NanjiIce();
    }

    @Provides
    IceBox provideIceBox(Ice ice) {
        System.out.println("provide ice box");
        return new HaierIceBox(ice);
    }
    ...
}

然后我们在CoffeeMaker中,只调用Componentinject方法,但是不去使用该对象:

class CoffeeMaker {
    @Inject
    Heater heater;
    @Inject
    Lazy<Pumb> pump; // 使用lazy
    @Inject
    Provider<IceBox> iceBox; // 使用provider
    @Inject
    @Type("shuiguo")
    Milk shuiguoMilk;

    CoffeeMaker() {
        CoffeeComponent component = DaggerCoffeeComponent.create();
        component.inject(this);
    }

    public void brew() {
//        heater.on();
//        pump.pump();
        System.out.println(" [_]P coffee! [_]P ");
//        iceBox.addIce();
//        shuiguoMilk.addMilk();
//        heater.off();
    }
}

执行结果:

04-21 15:03:11.684 17811-17811/com.charon.stplayer I/System.out: provide heater
04-21 15:03:11.694 17811-17811/com.charon.stplayer I/System.out:  [_]P coffee! [_]P 

可以看到普通注入只要声明就会被初始化,而使用ProviderLazy包装的并没有进行初始化, 接下来我们分别对这些不同对象调用两次:

class CoffeeMaker {
    @Inject
    Heater heater;
    @Inject
    Lazy<Pump> pump;
    @Inject
    Provider<IceBox> iceBox;
    @Inject
    @Type("shuiguo")
    Milk shuiguoMilk;

    CoffeeMaker() {
        CoffeeComponent component = DaggerCoffeeComponent.create();
        component.inject(this);
    }

    public void brew() {
        heater.on();
        System.out.println(" heater on ");
        heater.on();
        System.out.println(" heater on ");
        pump.get().pump();
        System.out.println(" pump pump ");
        pump.get().pump();
        System.out.println(" pump pump ");
        System.out.println(" [_]P coffee! [_]P ");
        iceBox.get().addIce();
        System.out.println(" iceBox addIce ");
        iceBox.get().addIce();
        System.out.println(" iceBox addIce ");
        shuiguoMilk.addMilk();
        heater.off();
    }
}

执行结果:

provide heater   // 一共执行了一次provide heater
~ ~ ~ heating ~ ~ ~
 heater on 
~ ~ ~ heating ~ ~ ~
 heater on 
provide pump     // 一共执行了一次provide pump
=> => pumping => =>
 pump pump 
=> => pumping => =>
 pump pump 
 [_]P coffee! [_]P 
provide ice         // 执行了第一次provide ice 和 provide icebox
provide ice box
加冰了,心飞扬
 iceBox addIce      // 调用了第一次iceBox.addIce方法
provide ice         // 执行了第二次provide ice 和 provide icebox
provide ice box
加冰了,心飞扬
 iceBox addIce      // 调用了第二次iceBox.addIce方法
添加:caomei牛奶

可以看到使用Provider包装的类,每次调用都会重新获取新的实例,而使用普通注入和使用Lazy包装都使用的是用一个实例。

当然,如果限定局部单例之后,无论是Provider还是Lazy,在同一个Activity中只会获取同一个依赖对象.

关于Dagger的部分基本都讲完了,后面就是讲DaggerAndroid的结合了。这里总结一下Dagger的依赖注入规则是:

  • 1.查找Module中是否存在创建该类的方法。
  • 2.若存在创建类方法,查看该方法是否存在参数
    • 2.1若存在参数,则按从步骤1开始依次初始化每个参数
    • 2.2若不存在参数,则直接初始化该类实例,一次依赖注入到此结束
  • 3若不存在创建类方法,则查找Inject注解的构造函数,看构造函数是否存在参数
    • 3.1若存在参数,则从步骤1开始依次初始化每个参数
    • 3.2若不存在参数,则直接初始化该类实例,一次依赖注入到此结束

依赖对象的注入源应该是有两个,一是Module中的@Provides方法,而是使用@Inject注解的构造函数。从前面示例中可以看出,如果使用@Scope注解类同时使用@Inject注解构造函数时,第一次创建时调用构造函数,然后将创建的实例缓存。以后再依赖注入时,不再调用构造函数创建,而是取缓存中的实例。根据Dagger的依赖注入规则,Module中的@Provides方法的优先级高于@Inject,那么使用@Scopre使用注解@Provides方法会产生什么样的效果呢?

可选的绑定(Optional bindings)

如果你想某个绑定在component的某些依赖不满足的情况下也能工作,可以给Module添加一个@BindsOptionalOf方法:

@BindsOptionalOf abstract CoffeeCozy optionalCozy();

这就意味着@Inject构造器和成员和@Provides方法可以依赖一个Optional<CoffeeCozy>对象,如果component绑定了CoffeeCozy那么Optional就是当前的,否则Optional就是缺省的。你可以注入一下几种类型:

Optional<CoffeeCozy>
Optional<Provider<CoffeeCozy>>
Optional<Lazy<CoffeeCozy>>
Optional<Provider<Lazy<CoffeeCozy>>>

绑定实例(Binding Instances)

如果你想在绑定component时注入参数,如app需要一个用户名参数,就可以给componentbuilder方法添加一个[@BindsInstance][BindsInstance]方法注解以使用户名字符串实例可以被注入到component中:

@Component(modules = AppModule.class)
interface AppComponent {
  App app();

  @Component.Builder
  interface Builder {
    @BindsInstance Builder userName(@UserName String userName);
    AppComponent build();
  }
}

public static void main(String[] args) {
  if (args.length > 1) { exit(1); }
  App app = DaggerAppComponent
      .builder()
      .userName(args[0])
      .build()
      .app();
  app.run();
}

编译时验证(Compile-time Validation)

Dagger2注解处理器是严格模式的,如果所有的绑定无效或者不完整就会导致编译时错误。例如这个module被安装到component,但是忘了绑定Executor:

@Module
class DripCoffeeModule {
  @Provides static Heater provideHeater(Executor executor) {
    return new CpuHeater(executor);
  }
}

那么当编译时,javac就会拒绝这个错误的绑定:

[ERROR] COMPILATION ERROR :
[ERROR] error: java.util.concurrent.Executor cannot be provided without an @Provides-annotated method.

只需要在当前component的任一Module中新增一个Executor@Provides方法即可修复这个错误。

@Binds@Provides

相信大家经常会使用@Provides来在Module里面提供需要注入对象的构造, 但从来没有用过@Binds.

如果我们需要注入一个接口的实现,我们常常会这么做:

@Module
public class HomeModule {

  @Provides
  public HomePresenter providesHomePresenter(){
    return new HomePresenterImp();
  }
}

其实这样的代码可以通过@Binds简化为:

@Module
public abstract class HomeModule {

  @Binds
  public abstract HomePresenter bindHomePresenter(HomePresenterImp homePresenterImp);
}

同时你需要将你的Module改为abstract即可,但是要注意这两个不能共存于一个Module,是不是很简单.

下一篇:Dagger2Android示例代码(六)