Dagger2使用简析(一)——简单注入

NOTE1 : Dagger2的官网地址: https://google.github.io/dagger/

NOTE2 : 使用前请先了解JAVA中的注解基本知识,如果对实现原理感兴趣可以了解下APT技术(不了解也不影响正常使用)

NOTE3: 本文所涉及的示例已上传至Github:
https://github.com/bailandi/Dagger2Demo


在具体使用前,首先来了解一个概念——依赖注入
依赖注入是由于类之间的依赖关系产生的,比如:

//产品依赖于操作工
public class Product{
  private Worker mWorker;
}

这个时候想要产生mWorker的实例,通常有两种方法:

//直接new
mWorker = new Worker();

//通过参数传入
public  Product(Worker worker){
  mWokrer = worker;
}

以上就是典型的依赖注入,那么为什么要使用Dagger2来进行依赖注入呢?
在回答这个问题前,我们首先分析以上两种产生mWorker实例的方式存在什么问题。

  • 对于第一种方式,假设当业务扩充,此时Worker的构造需要依赖于工具小刀KnifeWorker的构造函数发生变化,我们不得不对Product做出修改,这严重违反了开闭原则
  • 对于第二种方式,假设工厂Factory依赖于Product,此时Product的位置就会与第一种的Worker一样。我们可以从迪米特原则(最少知识原则)——一个类应该对自己直接耦合(依赖)的类知道最少中找到这一问题产生的根本原因,显然,Factory并不关心Worker,然而Product却在构造时将对Wokrer的依赖带入到了Factory
    依赖关系.png

为了解决这种依赖关系对业务扩展时造成的糟糕体验,我们使用Dagger2来进行依赖注入


一切实践从问题出发,首先我们来解决第一个问题,

1. 如何使用Dagger2注入一个简单对象到目标类中

让我们先复制一个示例,它包含下面三部分

@Module
public class MainModule {
    @Provides
    public Product provideProduct() {
        return new Product();
    }
}

@Component(modules = {MainModule.class})
public interface MainComponent {
    void inject(MainActivity activity);
}

public class MainActivity extends AppCompatActivity {
    @Inject
    Product mProduct;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        //...省略无关代码

        DaggerMainComponent
                .builder()
                .build()
                .inject(this);

        mProduct.create();
    }
}

我们依次对三个部分所涉及的内容进行解释:

1.1 @Module

@Module注解用于获取对象实例的类,Dagger2根据该注解知道应该去哪个类里获取对象实例

1.2 @Provides

@Provides注解用于module类中获取对象实例的方法,Dagger2根据该注解及方法的返回值类型将对象实例注入到对应的引用中

1.3 @Inject

@Inject用于注解构造函数或成员变量

  • 当作用于成员变量时,Dagger2根据该注解及成员变量的类型,从moudle中得到相应实例。
    注意:成员变量的访问修饰符不能是private

  • 当作用于构造函数时,就是Dagger2对于获取对象实例的第二种方式,比如上面的例子其实可以直接在Product的构造函数上注解@Inject,并移除@Provides注解的方法

     @Inject
     public Product() {
     }
    

    但是@Inject并非适用于所有的地方,比如以下三种情况:

    • 不具有构造函数的接口
    • 远程引入的三方库中的类无法被添加注解
    • 通过建造者模式等方式可配置化的进行构造的对象

    而使用@Provides可以更好的处理这些问题

1.4 @Component

@Component注解用于担任连接桥梁的接口,其两端分别是在@Component的参数中指定的modules数组在方法参数中指定的具体类
注意:方法参数必须是要注入的具体类,而非其父类或接口

1.5 DaggerMainComponent

在创建Module和Component后,我们需要build ——> Make Project,这样APT会在指定目录下生成对应的java文件,其中就会包含名如DaggerXX的的文件,它实现了我们所创建的Component接口,最终要完成注入,只需要像这样调用:

  DaggerMainComponent
                .builder()
                .build()
                .inject(this);

2. 简单分析自动生成的代码

2.1 DaggerXX类

public final class DaggerMainComponent implements MainComponent {
  private final MainModule mainModule;

  private DaggerMainComponent(MainModule mainModuleParam) {
    this.mainModule = mainModuleParam;
  }

  public static Builder builder() {
    return new Builder();
  }

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

  @Override
  public void inject(MainActivity activity) {
    injectMainActivity(activity);
  }

  private MainActivity injectMainActivity(MainActivity instance) {
    MainActivity_MembersInjector.injectMProduct(
        instance, MainModule_ProvideProductFactory.provideProduct(mainModule));
    return instance;
  }

  public static final class Builder {
    private MainModule mainModule;

    private Builder() {}

    public Builder mainModule(MainModule mainModule) {
      this.mainModule = Preconditions.checkNotNull(mainModule);
      return this;
    }

    public mainComponent build() {
      if (mainModule == null) {
        this.mainModule = new mainModule();
      }
      return new DaggerMainComponent(mainModule);
    }
  }
}

它实现了我们所创建的Component接口,内部使用了建造者模式,方便根据不同需求构建该对象实例,比如我们可以传入module实例替换默认的无参实例,关键看一下我们定义的inject方法的具体实现。

2.2 MainActivity_MembersInjector类和MainModule_ProvideProductFactory类

public final class MainActivity_MembersInjector implements MembersInjector<MainActivity> {
  //...省略无关代码

  public static void injectMProduct(MainActivity instance, Product mProduct) {
    instance.mProduct = mProduct;
  }
}
public final class MainModule_ProvideProductFactory implements Factory<Product> {
   //...省略无关代码

  public static Product provideProduct(MainModule instance) {
    return Preconditions.checkNotNull(
        instance.provideProduct(), "Cannot return null from a non-@Nullable @Provides method");
  }
}

其实关键的方法就是injectMProduct()provideProduct(),一看就懂,没什么可细说的。

3. 含有参数(依赖关系)的构造

Product必须与Worker关联时,有如下构造函数

 public Product(Worker worker) {
     mWorker = worker;
 }

此时我们提供Product实例对象时需要作出以下修改

@Provides
public Product provideProduct(Worker worker) {
    return new Product(worker);
}

即我们需要传入Worker来构建Product,那么Worker实例如何提供呢?其实同样的,我们可以使用简单构造时的两种方式提供Worker实例,具体就不在赘述,可以自行实践。

下一篇
,我们将对问题进行升级,并了解@Scope、@Qualifier、@binds、dependencies、Lazy的使用

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,544评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,430评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,764评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,193评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,216评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,182评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,063评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,917评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,329评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,543评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,722评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,425评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,019评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,671评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,825评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,729评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,614评论 2 353

推荐阅读更多精彩内容