当前位置: 首页 > news >正文

Dagger2基础篇(一)

一什么是依赖注入

Dagger2是依赖注入框架。很显然,知道了什么是依赖注入,就知道这个框架是为了解决什么问题而生。

那么什么是依赖注入?

其实很简单,这里我先用代码举个例子,然后我们再说定义:

比如一个人出门,他需要开车,那么先去创建一个Person类,然后再去创建一个Car类代码如下:

public class Person {

    private Car car;

    public Person() {
        car = new Car();
    }

    public void chumen() {

        car.drive();
    }
}复制代码
public class Car {

    public Car() {

    }

    public void drive() {
        Log.e("rrrrrr", "开汽车出门");
    }
}复制代码

人需要借助Car类完成出门这个需求,这种关系我们称为Person类对Car类依赖。

那么什么是注入呢?

有一天,人需要乘坐火车出远门,代码如下:

public class HotCar {

    public HotCar() {
    }

    public void drive() {
        Log.e("rrrrrr", "乘火车出远门");
    }
}复制代码
public class Person {

//    private Car car;
    
    private HotCar hotCar;

    public Person() {
//        car = new Car();
        
        hotCar= new HotCar();
    }

    public void chumen() {

//        car.drive();
        hotCar.drive();
    }
}复制代码

人需要借助HotCar类完成出门这个需求,Person类对HotCar依赖。你会发现,每次都要因为选择不同的依赖工具而修改Person类,很繁琐,于是我们想到把需要依赖的对象通过外部传递进来,修改代码如下:

public interface Drivors {
    
     void drive();
}
复制代码
public class Car implements Drivors {

    public Car() {

    }

    @Override
    public void drive() {
        Log.e("rrrrrr", "开汽车出门");
    }
}
复制代码
public class HotCar implements Drivors {

    public HotCar() {
    }
    @Override
    public void drive() {
        Log.e("rrrrrr", "乘火车出远门");
    }
}
复制代码
public class Person {

    private Drivors drivors;

    public Person(Drivors drivors) {
        this.drivors = drivors;
    }

    public void chumen() {

        drivors.drive();
    }
}复制代码

使用上边代码:

HotCar hotCar = new HotCar();
Person person = new Person(hotCar);
person.chumen();复制代码

你会发现不管换用什么工具出门,我们不需要再修改Person类中的代码,Person所依赖的对象由外部传入,这种效果我们叫依赖注入。

依赖注入的定义:内部不实例化依赖的对象,由外部传入的写法叫做依赖注入。

我们在日常开发中依赖注入的写法有那些呢?

1通过构造方法注入(传递)

class A {

    B b;
    C c;

    public A(B b, C c) {
        this.b = b;
        this.c = c;
    }
}复制代码

2通过setter方法注入(传递)

class A {
    B b;
    C c;
    
    public void setB(B b) {
        this.b = b;
    }


    public void setC(C c) {
        this.c = c;
    }

}复制代码

通过上边的简单代码,我们直到了什么是依赖注入,也知道了依赖注入的方式。

二Component 和Inject去注入对象

我们继续回到开始:Dagger2是依赖注入。我们似乎明白了Dagger2干的事情:就是把需求类内对象赋值的过程。

到此我们了解的Dagger2是为了解决什么问题而诞生的。为依赖注入而诞生

接下来我们一步步去了解怎么通过配置注解,然后完成最简单的依赖注入。

首先我们要添加Dagger2依赖如下:

implementation 'com.google.dagger:dagger:2.7'
annotationProcessor 'com.google.dagger:dagger-compiler:2.7'复制代码

然后我们去创建一个Car类

public class Car {
    @Inject
    public Car() {

    }
    public void drive() {
        Log.e("rrrrrr", "开汽车出门");
    }
}复制代码

然后给构造方法上添加一个@Inject注解,

对应的OneActivity代码如下:

public class OneActivity extends AppCompatActivity {

    @Inject
    Car car;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_one);
        car.drive();

    }
}复制代码

使用@Inject去注解这个需要注入的成员变量,下边使用这个对象。运行发现他会报空指针异常。

这个时候我们要去创建一个接口把他们关联起来,代码如下:

@Component
public interface A01Component {
    void inject(OneActivity activity);
}复制代码

使用@Component 注解一个接口,接口中定义一个传入需要注入类的对象。然后再需要注入的类中写下如下代码:

DaggerA01Component.builder().build().inject(this);复制代码

继续运行,发现不报错,并且可以调用drive方法,说明我们把对象注入成功。

上边对应的Car构造方法没有参数,但是如果Car有参数,我们怎么才能注入呢?

这个时候我们就需要去创建一个model类对Dagger2提供这个参数。代码如下:

public class Car {
    private  String name;

    @Inject
    public Car(String name) {
        this.name = name;

    }

    public void drive() {
        Log.e("rrrrrr", name+"开汽车出门");
    }
}复制代码
@Module
public class A01Module {

    private String name;

    public A01Module(String name) {
        this.name = name;
    }

    @Provides
    public String provideName() {
        return name;
    }


}复制代码

然后把Module类和Component类进行关联,如下:

@Component(modules = A01Module.class)
public interface A01Component {

    void inject(OneActivity activity);
}复制代码

最后使用如下:

public class OneActivity extends AppCompatActivity {

    @Inject
    Car car;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_one);
        DaggerA01Component.builder().a01Module(new A01Module("李四")).build().inject(this);

        car.drive();

    }
}复制代码

就完成有参数的类的注入。

三查看Dagger2生成的源码

上边我们通过一个接口就实现了赋值,既然是接口,就一定有实现类,关于实现类的生成涉及到另外一个技术APT技术(通过反射解析注解,生成代码的过程就叫做APT技术)。这里我们不去研究怎么生成的代码,而是去查看源码里边的逻辑。

那么源码怎么找呢?

Project视图下--build--source--apt--包名下就是Dagger2生成的代码:有一下三个文件:

public enum Car_Factory implements Factory<Car> {
  INSTANCE;

  @Override
  public Car get() {
    return new Car();
  }

  public static Factory<Car> create() {
    return INSTANCE;
  }
}复制代码
public final class DaggerA01Component implements A01Component {
  private MembersInjector<OneActivity> oneActivityMembersInjector;

  private DaggerA01Component(Builder builder) {
    assert builder != null;
    initialize(builder);
  }

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

  public static A01Component create() {
    return builder().build();
  }

  @SuppressWarnings("unchecked")
  private void initialize(final Builder builder) {

    this.oneActivityMembersInjector = OneActivity_MembersInjector.create(Car_Factory.create());
  }

  @Override
  public void inject(OneActivity activity) {
    oneActivityMembersInjector.injectMembers(activity);
  }

  public static final class Builder {
    private Builder() {}

    public A01Component build() {
      return new DaggerA01Component(this);
    }
  }
}复制代码
public final class OneActivity_MembersInjector implements MembersInjector<OneActivity> {
  private final Provider<Car> carProvider;

  public OneActivity_MembersInjector(Provider<Car> carProvider) {
    assert carProvider != null;
    this.carProvider = carProvider;
  }

  public static MembersInjector<OneActivity> create(Provider<Car> carProvider) {
    return new OneActivity_MembersInjector(carProvider);
  }

  @Override
  public void injectMembers(OneActivity instance) {
    if (instance == null) {
      throw new NullPointerException("Cannot inject members into a null reference");
    }
    instance.car = carProvider.get();
  }

  public static void injectCar(OneActivity instance, Provider<Car> carProvider) {
    instance.car = carProvider.get();
  }
}复制代码

通过上边源码可以看出,把创建的Car对象和传入的OneActivity对象一起传入OneActivity_MembersInjector类中,然后在injectMembers方法中完成的赋值。

上边是一个通过Dagger2实现依赖注入的最简单例子。

四第三方库怎么用Dagger2注入

通过上边的两个例子我们发现,@Component 和 @Inject 的配合就能够使用 Dagger2 了,但这里面存在一个局限,@Inject 只能标记在我们自己编写的类的构造方法中,如果我们使用第三方的库或者标准库的话,我们就没有办法用这两个注解完成依赖注入了,这时候,我们就需要新的注解@Provides 和 @Module

接下来的需求大概是这样,我们假如Person类是第三方库中的类,不让Person类中出现任何注解,然后使用Dagger去注入

public class Pseson {

    public Pseson() {
    }

    public String getName() {
        return "张三";
    }
}复制代码

然后创建Component

@Component
public interface A02Component {

    void inject(TwoActivity activity);
}复制代码

最后使用:

public class TwoActivity extends AppCompatActivity {

    @Inject
    Pseson person;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_one);
        DaggerA02Component.builder().build().inject(this);
        person.getName();

    }
}复制代码

这个时候编译发现失败,是因为Person构造没有添加注解。这个时候我们就需要去创建一个Model类

@Module
public class A02Module {

    @Provides
    public Pseson providePerson() {
        return new Pseson();
    }
}复制代码

值得注意的地方有
- @Provides 修饰的方法一般用 provide 作用方法名前缀。
- @Module 修饰的类一般用 Module 作为后缀。

在 @component 注解后面的括号中取值module.class。

@Component(modules = A02Module.class)
public interface A02Component {

    void inject(TwoActivity activity);
}复制代码

这样我们就通过这个四个注解完成了第三方类的对象注入。

如果第三方库中构造方法也有参数,这个我们怎么注入呢?假如上边的Person依赖Car类。

我们就需要修改我们module中的代码,也去提供这个参数即可。修改后的module如下:

@Module
public class A02Module {

    @Provides
    public Pseson providePerson(Car car) {
        return new Pseson(car);
    }

    @Provides
    public Car provideCar() {

        return new Car("");
    }
}复制代码

这样就完成了第三方库构造方法有参数的注入

五 两种注入方式,那么他们有没有优先级呢?

当然是有的!他们的顺序如下:

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

通过上边的学习我们学会了两种注入方式的基本使用,以及他们之间的优先级和如果有参数情况下怎么提供参数。我相信通过这篇文章的阅读,你的Dagger2一定能达到入门级别,知道这个东西是什么,简单怎么使用。后续还会有两篇文章更新,第二篇讲解一些高级的用法。第三篇讲解怎样把Dagger2融合到mvp项目中去。

代码下载地址:github.com/XiFanYin/Da…




相关文章:

  • CreatorPrimer|从zIndex开始
  • (day6) 319. 灯泡开关
  • python其他模块安装
  • jQuery普通绑定事件和on委托事件对比
  • 微信小程序实例:分享给一个人还是分享到群的判断代码
  • 线程与进程的区别(基础面试题)
  • C#将控件置于最顶层和最底层
  • 带有去重以及字符串拼接、日期拼接、字段相除的SQL语句
  • 面试题收集最新
  • js 数组排序
  • Andrew Ng-ML-第十九章-应用举例:照片OCR(光学字符识别)
  • 泛型的理解(1)
  • 使用阿里云接口进行手机号(三网)实名认证
  • unique()函数使用
  • Install MongoDB to Ubuntu 18.04
  • [deviceone开发]-do_Webview的基本示例
  • 【402天】跃迁之路——程序员高效学习方法论探索系列(实验阶段159-2018.03.14)...
  • ➹使用webpack配置多页面应用(MPA)
  • co.js - 让异步代码同步化
  • es6--symbol
  • iOS编译提示和导航提示
  • Java多态
  • Java应用性能调优
  • js如何打印object对象
  • leetcode讲解--894. All Possible Full Binary Trees
  • PV统计优化设计
  • Synchronized 关键字使用、底层原理、JDK1.6 之后的底层优化以及 和ReenTrantLock 的对比...
  • Vultr 教程目录
  • 表单中readonly的input等标签,禁止光标进入(focus)的几种方式
  • 大数据与云计算学习:数据分析(二)
  • 看图轻松理解数据结构与算法系列(基于数组的栈)
  • 理解 C# 泛型接口中的协变与逆变(抗变)
  • 网络应用优化——时延与带宽
  • 问:在指定的JSON数据中(最外层是数组)根据指定条件拿到匹配到的结果
  • 线上 python http server profile 实践
  • 掌握面试——弹出框的实现(一道题中包含布局/js设计模式)
  • No resource identifier found for attribute,RxJava之zip操作符
  • #我与Java虚拟机的故事#连载02:“小蓝”陪伴的日日夜夜
  • (3)llvm ir转换过程
  • (C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...
  • (day 12)JavaScript学习笔记(数组3)
  • (day 2)JavaScript学习笔记(基础之变量、常量和注释)
  • (ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_READ_ONLY)讲解
  • (二)【Jmeter】专栏实战项目靶场drupal部署
  • (官网安装) 基于CentOS 7安装MangoDB和MangoDB Shell
  • (十八)三元表达式和列表解析
  • (转)eclipse内存溢出设置 -Xms212m -Xmx804m -XX:PermSize=250M -XX:MaxPermSize=356m
  • (状压dp)uva 10817 Headmaster's Headache
  • ***详解账号泄露:全球约1亿用户已泄露
  • .net 打包工具_pyinstaller打包的exe太大?你需要站在巨人的肩膀上-VC++才是王道
  • .Net程序帮助文档制作
  • .net最好用的JSON类Newtonsoft.Json获取多级数据SelectToken
  • .skip() 和 .only() 的使用
  • ??javascript里的变量问题
  • ??如何把JavaScript脚本中的参数传到java代码段中