Dagger2 使用总结(一)

Dagger2是一套依赖注入框架,在编译期间自动生成代码,创建依赖的对象。项目中使用Dagger2可以降低代码的耦合度。
使用Dagger2库,重点是了解其中的各种注解并熟练使用,下面看一下具体用例。

注:为了能够更直观地了解Dagger2的使用,本文用例会尽量简化不相关的业务逻辑

环境配置


配置下build.gradle即可

dependencies {
    implementation 'com.google.dagger:dagger:2.14.1'
    annotationProcessor 'com.google.dagger:dagger-compiler:2.14.1'
}

 

@Inject和@Component注解


使用@Inject@Component注解是Dagger2最基本的使用方式,来看一个简单的Demo,向 MainActivity 里注入 Apple

 

  • 编写 Apple类代码
public class Apple {

    @Inject   //构造器注入注解,在被注入对象的构造器上标注
    public Apple() {

    }

    public void print() {
        Log.d(TAG,"This is an apple");
    }
}

 

  • 编写 MainActivity 类代码
@Inject //属性注入注解,在需要注入的属性上标注,不能为private,如果需要用private可以使用方法注入,下面会讲
public Apple apple;

@Component  //用于连接注入类和目标类的接口,建议命名:目标类名+Component
interface MainActivityComponent {
    void inject(MainActivity activity);
}

 

  • 编译项目

这时候编译项目会在build目录下生成以下文件:
Apple_Factory.java
DaggerMainActivity_MainActivityComponent.java
MainActivity_MembersInjector.java
这些文件是Dagger2框架帮我们自动生成的,用于帮我们注入依赖。

 

  • MainActivity 注入依赖

使用依赖注入

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

    //给之前定义的apple注入依赖
    DaggerMainActivity_MainActivityComponent.builder().build().inject(this);
    apple.print();

}

如果不使用依赖注入,那写法将会是下面这样:

//直接new出对象,耦合性高,在项目较复杂的情况下扩展性较差
apple = new Apple();

这样看起来比不使用Dagger2注入对象要简单很多,但是如果对象引用的地方很多,构造复杂,那么一旦改变构造,工作量会很大,这也是Dagge2最重要的优势——解耦。

  • 小结

以上是Dagger2最简单的使用方法,注入依赖后,会用在Apple类中标注@Inject注解的构造器自动创建apple对象。而@Component标注的接口用于连接目标类和注入类的工厂Apple_Factory

  • 方法注入

如果注入的对象需要设置为private,那我们可以使用方法注入,示例:

private Apple apple;

@Inject
public void setApple(Apple apple) {
    this.apple = apple;
}

上述代码作用等同于:

@Inject
public Apple apple;
  • 拓展

如果现在需要修改Apple类的构造器,需要加一个Color参数,如下:

public class Apple {

    public Color color;

    @Inject
    public Apple(Color color) {
        this.color = color;
    }

    public void print() {
        Log.d(TAG, "This is an apple");
    }
}

这时候构造器中的color也需要注入依赖,创建一个简单的Color类并在构造器上标注注解@Inject即可

public class Color {
    @Inject
    public Color() {

    }    
}

这样在构建apple对象时会寻找标注了@Inject注解的Color的构造器新建color对象用于apple对象的构造。
除了@Inject@Component之外,Dagger2中还有其他注解,下文会说明。

 

@Modele和@Provides注解


@Inject注解存在局限性,以下两种情况时不能使用:

  • 注入的对象来自第三方库
  • 注入的对象声明为抽象类或接口(依赖倒置原则)

比如上述的例子修改下,添加一个抽象类Fruit

public abstract class Fruit {
    public abstract void print();
}

修改后Apple类继承Fruit

public class Apple extends Fruit {

    @Inject
    public Apple() {

    }

    @Override
    public void print() {
        Log.d(TAG,"This is an Apple");
    }
}

这时候,如果只按照下面的形式声明,是无法注入apple对象的,运行会报错。

@Inject
public Fruit apple;

这时候就需要用到@Modele@Provides注解,完整示例如下:

  • 创建Fruit类和Apple

Fruit抽象类:

public abstract class Fruit {
    public abstract void print();
}

Apple类:

public class Apple extends Fruit {

    //这里注意可以不再标注@Inject
    public Apple() {

    }

    @Override
    public void print() {
        Log.d(TAG,"This is an Apple");
    }
}

 

  • 创建Module类
@Module //注解提供注入对象的类
public class FruitModule {
    @Provides //注解方法,方法返回注入的对象
    Fruit provideFruit() {
        return new Apple();
    }
}

 

  • MainActivity中注入对象
@Inject //和之前一样的注解
public Fruit apple;

@Component(modules = FruitModule.class) //这里注意,需要加上Module类
interface MainActivityComponent {
    void inject(MainActivity activity);
}

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

    //和之前一样的方式注入对象
    DaggerMainActivity_MainActivityComponent.builder().build().inject(this);
    apple.print();
}

 

  • 小结

使用@Inject@Component注解注入对象时,我们需要在声明的注入类的构造器上加上@Inject注解来使对象被创建。
如果遇到注入类构造器不方便编辑时(比如来自三方库,抽象类,接口),则可以使用@Module@Provides注解,将对像获得由构造器获得转变为由方法返回,使对象的创建形式更加可控。

再形象看一下两者的区别(不想看可以跳过):

仅使用@Inject@Component注入对象build目录下生成的文件如下
Apple_Factory.java
DaggerMainActivity_MainActivityComponent.java
MainActivity_MembersInjector.java

使用@Module@Provides注入对象build目录下生成的文件如下
FruitModule_ProvideAppleFactory
DaggerMainActivity_MainActivityComponent.java
MainActivity_MembersInjector.java

区别在于前者是Apple_Factory.java而后者是FruitModule_ProvideAppleFactory,这两个类都是用于提供对象的,而DaggerMainActivity_MainActivityComponent.java类实现了MainActivityComponent接口,用于将目标类和提供对象的类连接起来。所以两者的区别仅在于提供对象的方式不同,再具体可以看这两个差异类的实现。

  • 拓展

Component接口可以指定多个Module类,便于将它们一起注入,比如在这个例子中,可以按下述方式编写:

@Component(modules = {FruitModule.class, OtherModule.class}) 
interface MainActivityComponent {
    void inject(MainActivity activity);
}

这样我们便可以注入OtherModule中可以获得的对象。

 

@Named注解


上述的@Module@Provides注解仅能返回一个继承了Fruit的对象,如果再加入一个Banana类,继承Fruit并在MainActivity中注入,这时候就需要使用@Named注解。基于上述代码,示例如下:

  • 新建Banana
public class Banana extends Fruit {
    @Override
    public void print() {
        Log.d(TAG, "This is a Banana");
    }
}

 

  • 修改FruitModule
@Module
public class FruitModule {

    @Provides
    @Named("Apple") //用@Named注解标注返回Apple的方法
    Fruit provideApple() {
        return new Apple();
    }

    @Provides
    @Named("Banana") //用@Named注解标注返回Banana的方法
    Fruit provideBanana() {
        return new Banana();
    }

}

 

  • 修改MainActivity
@Inject
@Named("Apple")
public Fruit apple;  //用@Named注解标注这个声明注入的为Apple

@Inject
@Named("Banana")
public Fruit banana; //用@Named注解标注这个声明注入的为Banana

@Component(modules = FruitModule.class)
interface MainActivityComponent {
    void inject(MainActivity activity);
}

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

    DaggerMainActivity_MainActivityComponent.builder().build().inject(this);
    apple.print();
    banana.print();
}

 

  • 小结

@Named注解使用比较简单,只要将Module类中的标注和目标类中声明注入类的标注一一对应即可。

 

@Qualifier注解


@Qualifier注解的作用和@Named相同,只是实现有所区别,直接看示例:

  • 修改FruitModule类
@Module
public class FruitModule {

    @Qualifier //定义Apple的注解
    @Retention(RetentionPolicy.RUNTIME)
    public @interface ProvideApple{}

    @Qualifier //定义Banana的注解
    @Retention(RetentionPolicy.RUNTIME)
    public @interface ProvideBanana{}

    @Provides
    @ProvideApple  //表示这个方法返回Apple
    Fruit provideApple() {
        return new Apple();
    }

    @Provides
    @ProvideBanana //表示这个方法返回Banana
    Fruit provideBanana() {
        return new Banana();
    }

}

 

  • 修改MainActivity类
@Inject
@FruitModule.ProvideApple  //表示这个对象注入Apple
public Fruit apple;

@Inject
@FruitModule.ProvideBanana //表示这个对象注入Banana
public Fruit banana;

@Component(modules = FruitModule.class)
interface MainActivityComponent {
    void inject(MainActivity activity);
}

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

    DaggerMainActivity_MainActivityComponent.builder().build().inject(this);
    apple.print();
    banana.print();
}

 

  • 小结

@Qualifier@Named注解作用是一样的,区别是@Named使用字符串来区分不同的返回对象,而@Qualifier用自定义的接口来区分,这样可以提高代码的可读性,且不容易出错。

 
内容有点多,不得不分成两篇总结了。
贴个链接:Dagger2 使用总结(二)

你可能感兴趣的:(Dagger2 使用总结(一))