前言
年前架构组的大佬们,分享了一个内容:如何让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();
}
Starks
、Boltons
中的@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();
}
然后我们可以这样使用我们的Cash
和Soldiers
。
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篇的内容,就放在下一篇文章中吧!
网友评论