破壳之Dagger2入门与使用

什么是 Dagger2

Dagger2 的核心是 依赖注入,对于 依赖注入 的理解,每一个人都有一个本身的版本,个人理解是:经过注入的方式得到想要的依赖。java

在开始学习使用 Dagger2 以前,咱们最好先理解清楚这个框架帮咱们作了什么,我在一开始使用 Dagger2 的时候也是这样,一上来无论三七二十一先看怎么用,结果致使的结果是用的过程当中一脸问号。缘由就是没有理解这个框架它到底是帮咱们干了什么。git

Dagger2 是一个能帮助咱们经过注解的方式,快速完成依赖注入的优秀框架。程序员

不过也别过于神化 Dagger2,要想使用好它,咱们仍是要遵循很多约定规则的,该写的代码也逃避不了。github

至于 Dagger2 好处,我以为最大的好处就是解藕了,最直观的一个解释是:哪天代码升级,须要修改一个在整个项目中大量被 new 的对象的构造方法时,若是用了 Dagger2 依赖注入,就能够不用大范围的修改代码,只要修改依赖提供者 Module 里面的 @Provides 注解的方法便可。固然上面说的都是有前提条件的,因此没必要较真于此,毕竟咱们的目的是理解并使用 Dagger2bash

下面先逐个击破 Dagger2 的注解。Let‘s go...app

Dagger2 的注解

@Inject

@Inject 注解会在两个地方被看到:框架

  1. 依赖的构造方法
  2. 依赖需求者的属性或者方法

先看代码:ide

// 车轮
public class Wheel {

  @Inject
  public Wheel(){
    Log.e("Wheel", "我是自行车轮胎");
  }

}

// 自行车
public class Bike {

  // 方式一
  @Inject
  Wheel mWheel;
  
  // 方式二
  //@Inject
  //public void setWheel(Wheel wheel){
  // this.mWheel = wheel;
  //}

  public Bike() {
    Log.e("Bike", "我是自行车");
    DaggerBikeComponent.builder().build().inject(this);
  }
}

// 自行车依赖注入器
@Component
public interface BikeComponent {
  // 这个方法名能够任意起,只是你们更习惯用 inject
  // 由于inject 比较形象
  void inject(Bike bike);
}
复制代码

自行车对象依赖车轮,咱们使用 @Inject 注解了 Wheel 的构造方法,以及 Bike 中的 mWheel 属性(或者被注释的方法),结合被 @Component 注解的 BikeComponent,咱们实现了最最简单的依赖注入代码。学习

DaggerBikeComponent 的起到桥梁的做用,链接 依赖需求者依赖提供者,后面会有更多讲解。ui

DaggerBikeComponentDagger2 自动帮咱们生成的,名称的规则是 Dagger{ Component 名称 }

@Inject 的一些约定:

  1. @Inject 不能使用 private,打开 build 后生成的代码咱们会找到缘由,这里留给你们本身验证;
  2. @Inject 修饰依赖构造方法的时候,若是存在多个构造方法,只能选择修饰一个。至于要使用多个构造方法,后面会有解决方案「???????」。

@Module & @Provides

能够看到,上面咱们为了获取 Wheel 依赖,是直接修改了 Wheel 的源码,在其构造方法上动了些手脚,加上了 @Inject 注解。可是若是咱们使用的是一个第三方库提供的类的话,@Inject 这招就行不通了。

@Module 登场!!!

先看代码:

// 【新加】车灯,没有被 @Inject 注解构造方法
// 三方提供
public class Light {

  public Light(){
    Log.e("Light", "我是车灯");
  }

}

// 自行车
public class Bike {

  @Inject
  Wheel mWheel;

  @Inject
  Light mLight;

  //@Inject
  //public void setWheel(Wheel wheel){
  // this.mWheel = wheel;
  //}

  public Bike() {
    Log.e("Bike", "我是自行车");
    // 【修改1】多了 .bikeModule(new BikeModule())
    DaggerBikeComponent.builder().bikeModule(new BikeModule()).build().inject(this);
  }
}

// 自行车依赖注入器 【修改2】指定了 modules 为 BikeModule
@Component(modules = {BikeModule.class})
public interface BikeComponent {
  void inject(Bike bike);
}

// 【新加】自行车依赖管理员
@Module
public class BikeModule {

  // 名字没有规定,
  // 通常为 provide+待被提供的依赖名称
  @Provides 
  Light provideLight(){
    return new Light();
  }

}
复制代码

能够将 @Module 注解的类理解为依赖管理员,这个管理员经过 @Provides 注解的方法提供依赖给依赖需求者,好比上面的例子,经过 @Provides 注解了 Light provideLight() 方法,该方法实际直接返回了一个 Light 对象( 这里就是我一开始讲的,该写的代码仍是逃避不了,好比建立这个依赖的代码仍是须要咱们手动的血出来 )。

Dagger2 在寻找依赖的时候有一个顺序,会先从 @Module 中寻找,若是找不到再从 @Inject 注解了构造方法的类寻找,这个了解一下就好,在定位一些问题的时候会派上用场

@Component

@Component 注解的类能够理解为一个桥梁,这个桥梁的做用是链接 Module(依赖管理员)和依赖需求者(好比依赖了 WheelBike ),没有这个中介,@Inject@Module 都会失去做用。

Component 会声明一个 Module 集合,也就是说,一个 Componet 能够有多个依赖管理员。

@Qualifier & @Named

@Qualifier 注解的做用按字面理解便知一二,Qualifier 是 “限定” 的意思,意思能够用它来限定一些东西,具体限定什么,先看下面的例子,Light 类多个一个灯色的属性:color, 默认颜色是白色。

public class Light {

  private String color = "white";

  public Light() {
    Log.e("Light", "我是车灯, 而且颜色是:" + this.color);
  }

  public Light(String color) {
    this.color = color;
    Log.e("Light", "我是车灯, 而且颜色是:" + this.color);
  }
}
复制代码

如今咱们的 Light 类有两个构造方法,改写一下依赖管理员:

@Module
public class BikeModule {

  @Provides 
  Light provideLight() {
    return new Light();
  }
  
  @Provides 
  Light provideRedLight() {
    return new Light("red");
  }
}
复制代码

这个时候咱们 run build 一下,会获得下面的编译错误:

xxx.xx.xxx.Light is bound multiple times...
复制代码

说明直接这样写确定不行,那么能够请 @Qualifier 登场了。首先咱们自定一个 Qualifier

@Qualifier
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface LightColorQualifier {
  String value() default "white";
}
复制代码

下面调整一下 Module@Inject 的地方就可让代码又从新跑起来了

@Module
public class BikeModule {

  @LightColorQualifier()
  @Provides
  Light provideLight() {
    return new Light();
  }

  @LightColorQualifier("red")
  @Provides
  Light provideRedLight() {
    return new Light("red");
  }
}


public class Bike {

  @Inject
  Wheel mWheel;

  @LightColorQualifier("red")
  @Inject
  Light mLight;

  //@Inject
  //public void setWheel(Wheel wheel){
  // this.mWheel = wheel;
  //}

  public Bike() {
    Log.e("Bike", "我是自行车");
    DaggerBikeComponent.builder().bikeModule(new BikeModule()).build().inject(this);
  }
}
复制代码

你能够试着改变 @LightColorQualifier("red")@LightColorQualifier("blue"), 会发现 build 又会失败,这说明这个 @LightColorQualifier("red") 起到了一个限定的做用,依赖管理员会根据这个限定去拿须要的依赖给需求者。

讲完 @Qualifier 的原理和做用,@Named 水到渠成,看源码可知:

@Qualifier
@Documented
@Retention(RUNTIME)
public @interface Named {
    /** The name. */
    String value() default "";
}
复制代码

是的,@Named 只是一个官方帮咱们实现好的 @Qualifier,没啥特殊要求,直接用这个便可,至于在定义本身的 @Qualifier 的时候,下面几个方面能够你们本身尝试探究一下:

  1. 我上面是参考的官法 @Named 用的 value() ,能否换成其余的名字?
  2. 能否定义多个变量?
  3. 变量除了 String 以外,还能够用其余类型吗,好比 intboolean 之类?

@Scope & @Singleton

这边就不绕弯子了,@Scope@Singleton 的关系,其实和上面的 @Qualifier@Named 同样, @Singleton 是一个官方 @Scope 实现:

@Scope
@Documented
@Retention(RUNTIME)
public @interface Singleton {}
复制代码

回到 Scope ,中文意思是 “范围”,使用 @Scope 能够约定一些声明周期,好比咱们开发中常常会用到 @ActivityScope@FragmentScope,可是是否是咱们只要使用了 @Singleton@ActivityScope@FragmentScope。。依赖就会自动有了生命周期了呢?

固然不会,仍是开头讲的,不能神化 Dagger2。。

@Singleton 并非真正意义上用来建立单列的。 其实 Dagger2 真正建立单列的流程是:

  1. 首先建立一个 Module 实现全局类的实现
  2. 而后在 AppComponent 中使用这个 Module
  3. 保证 AppComponent 只会初始化一次,通常会在 ApplicationonCreate() 方法中建立这个 AppComponent

那么 @Singleton 的做用是什么:

  1. 更好的管理 ModuleComponent 之间的关系,保证 AppComponentModule 是匹配的。若AppComponentModuleScope 是不同的,则会在编译时报错。
  2. 代码可读性,让别的人看代码的时候更好的了解 Module 中建立的类实例是单例。

最后整理一些约定:

  1. 若是在构造方法或者 @Provides 方法注解了 Scope@Component 必定要有注解
  2. @Component 注解了 Scope,不必定非得有构造方法或者 @Provides 方法被 Scope 注解
  3. @Scope@Component 能够依赖有 @Scope@Component,有 @Scope@Component 只能依赖有 @Scope@Component,而且二者的 @Scope 不能相同
  4. @Singleton@Component 只能被依赖而不能依赖任何 @Component

@Component 的复用

好的程序员怎能不会偷懒???

Component 的复用主要有下面两种方式:Component 中的 dependencies 属性和 @Subcomponent 注解

经过 dependencies 属性指定依赖的 Component

@ActivityScope
@Component(modules = {MainModule.class } , dependencies = {AppComponent.class})
public interface MainComponent {
  void inject(MainActivity mainActivity);
}


@Singleton
@Component(modules = { AppModule.class })
public interface AppComponent {
  Gson gson();
}
复制代码

这种方式,若是在 MainActivity 中想要获取到 AppModule 里面提供的依赖,必须在 AppComponent 里面显式的提供出来。否则的话是无法在 MainActivity 中获取到 AppModule 中想要依赖的。

@Subcomponent

@ActivityScope
@Subcomponent(modules = {MainModule.class })
public interface MainComponent {
  void inject(MainActivity mainActivity);
}


@Singleton
@Component(modules = { AppModule.class })
public interface AppComponent {
  MainComponent plus(MainModule mainModule);
}

public class MainActivity extends AppCompatActivity {

  @Inject Gson mGson;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    DemoApp.getInstance().getAppComponent().plus(new MainModule(this)).inject(this);

    //DaggerMainComponent.builder()
    // .appComponent(DemoApp.getInstance().getAppComponent())
    // .mainModule(new MainModule(this))
    // .build()
    // .inject(this);

    Log.e("MainActivity", mGson.toString());
    //new Bike();
  }
}
复制代码

@Subcomponent 注解的 Component 不会自动生成相似 DaggerxxxComponent 的类文件,实际上是把本身扔到了父 Component 中,从而得到到父 Component 的全部注入属性, 好比上面咱们就没有再显式的在 AppComponent 中提供 Gson 对象了。

经过这两个例子,你们能够对比体会一下两者的区别。我我的的理解是 dependencies 更加委婉,我打好招呼要啥再给啥,@Subcomponent 就有点粗暴了,直接本身跑到人家家里各类拿,想拿啥拿啥。

最后奉上例子代码

代码连接

小武的知识铺
相关文章
相关标签/搜索