Dagger2最详细的使用教程和源码分析(一)基本注入使用

2019-04-12  本文已影响0人  青墨一隅

1.导入依赖

implementation 'com.google.dagger:dagger:2.11'
annotationProcessor 'com.google.dagger:dagger-compiler:2.11'

最新版本请官网获取

2.什么是Dagger2?

3.为什么使用Dagger2?

常用的依赖注入

public class A {
    private B b;

    A(B b) {
        this.b = b;
    }
}
public class A {
    private B b;

    public void setB(B b) {
        this.b = b;
    }
}
//首先定义一个接口
public interface IC {
    void printMess();
}

//A
public class A implements IC {

    @Override
    public void printMess() {
        System.out.println("我的信息是A");
    }
}

//B
public class B implements IC {
    @Override
    public void printMess() {
        System.out.println("我的信息是B");
    }
}

//D
public class D {
    private IC ic;

    D(IC ic) {
        this.ic = ic;
        ic.printMess();
    }
}

/测试类
public class Test {
    public static void main(String[] args) {
        new D(new A());
        new D(new B());

    }
}

输出结果:
我的信息是A
我的信息是B
通过D类进行接口依赖注入,如果新增类去答应信息只需要在Test类中动态配置注入的参数就行。

你肯定会说,这三种依赖注入的方式已经解决了程序解耦的问题,为什么还会出来一个Dagger2呢?那是因为上面3中依赖注入的方式都不能解决一个问题,那就是面对A、B类在程序中大量并且多个地方创建我们需要大量的new操作,如果这个时候有一个需求需要改变构造参数你该怎么办呢?全局搜索一个个去改?刚改完需求又变动怎么办?好吧我承认我受不了......
那么我们的Dagger2就横空出世了,它通过java的注解的方式创建对象,省去new操作,在修改构造参数的时候只需要在一处修改即可,下面我们接着看如何实现。

public class DaggerBean {
    private int age;
    private String name;

    @Inject
    public DaggerBean() {
        age = 17;
        name = "张三";
    }

    public int getAge() {
        return age;
    }

    public String getName() {
        return name;
    }
}

public class DaggerActivity extends AppCompatActivity {
    @Inject
    DaggerBean bean;

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

    private void initView() {
        TextView content = (TextView) findViewById(R.id.tv_dagger);
        content.setText(bean.getName()+"今年"+bean.getAge()+"岁了");
    }
}

直接运行程序会发生空指针异常,因为直接通过@Inject 注入DaggerBean对象是不会成功的,因为DaggerActivity不知道去哪里找到它的实例去注入生成,这时我们需要一个连接器Component,让上面这两个类产生联系:

//创建一个Component
/**
 * 1.必须是接口
 * 2.必须使用Component注解
 */
@Component
public interface DaggerComponent {

    /**
     * inject名称可以更改
     * @param daggerActivity 将Activity对象和DaggerBean进行关联的方法
     */
    void inject(DaggerActivity daggerActivity);
}

build一下项目(必须先build),然后我们修改一下DaggerActivity中的代码

public class DaggerActivity extends AppCompatActivity {
    @Inject
    DaggerBean bean;

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

    private void initView() {

        //dagger2通过APT技术生成的类(后续我们会有专门的篇幅分析源码),进行注入
        DaggerDaggerComponent.builder()
                .build()
                .inject(this);

        TextView content = (TextView) findViewById(R.id.tv_dagger);
        content.setText(bean.getName() + "今年" + bean.getAge() + "岁了");
    }
}

运行结果


image.png

这一次运行成功了,是不是so easy!!!

@Module

尽管上面我们实现了操作但是同时我们也发现了一些问题:

  1. 在DaggerBean的构造函数需要使用@Inject进行注解,如果DaggerBean是系统类或者第三方库的类我们怎么办?
  2. 同时使用@Module和@Inject会以哪一个为主?
  3. 类的构造函数带有参数,直接使用@Inject会出现编译错误怎么办?

为了解决上面的疑惑我们的下一个主角@Module闪亮登场,@Module和@Inject属于两个不同的纬度,我们先看第1个问题,首先先创建一个DaggerModule

/**
 * 1.可以提供一些对象实例
 * 2.提供构造函数所需要的参数
 */
@Module
public class DaggerModule {

    /**
     * 必须使用Provides进行注解
     * @return 提供一个第三方库对象
     */
    @Provides
    public Gson provideGson(){
        return new Gson();
    }

}

修改一下DaggerActivity

public class DaggerActivity extends AppCompatActivity {
    @Inject
    DaggerBean bean;

    @Inject
    Gson gson;

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

    private void initView() {

        //dagger2通过APT技术生成的类(后续我们会有专门的篇幅分析源码),进行注入
        DaggerDaggerComponnet.builder()
                .build()
                .inject(this);

        TextView content = (TextView) findViewById(R.id.tv_dagger);
        content.setText(gson.toJson(bean));
        //content.setText(bean.getName() + "今年" + bean.getAge() + "岁了");
    }
    
}

运行项目显示结果


image.png

接下来看第2个问题,首先修改DaggerBean

public class DaggerBean {
    private int age;
    private String name;

    @Inject
    public DaggerBean() {
        age = 17;
        name = "张三";
    }

    public DaggerBean(int age,String name) {
        this.age = age;
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public String getName() {
        return name;
    }
}

修改


/**
 * 1.可以提供一些对象实例
 * 2.提供构造函数所需要的参数
 */
@Module
public class DaggerModule {

    /**
     * 必须使用Provides进行注解
     * @return 提供一个第三方库对象
     */
    @Provides
    public Gson provideGson(){
        return new Gson();
    }

    @Provides
    public DaggerBean provideDaggerBean(){
        return new DaggerBean(20,"李四");
    }

}

运行项目


image.png

从运行结果可以看出来,module纬度的优先级更高一些。

第三个问题特别需要注意一下,先看一下构造函数

    @Inject
    public DaggerBean(int age,String name) {
        this.age = age;
        this.name = name;
    }

如果我们直接把代码写成这样去编译使通不过的,因为我们没有给构造函数传参数,并且构造函数中如果不能使用基本数据类型去传参,下面看一下如果我们想传递构造函数的参数如何来使用:
新增一个类WrapDaggerData,进行包装数据

public class WrapDaggerData {
    public int getAge(){
        return 22;
    }

    public String getName(){
        return "王五";
    }
}

//更改一下构造参数
public class DaggerBean {
    private int age ;
    private String name ;

    @Inject
    public DaggerBean(WrapDaggerData wrapDaggerData) {
        age = wrapDaggerData.getAge();
        name = wrapDaggerData.getName();
    }
    
}

//修改一下
@Module
public class DaggerModule {

    /**
     * 必须使用Provides进行注解
     * @return 提供一个第三方库对象
     */
    @Provides
    public Gson provideGson(){
        return new Gson();
    }

    /**
     * 这里是提供构造参数的地方
     * @return
     */
    @Provides
    public WrapDaggerData provideDaggerGsonData(){
        return new WrapDaggerData();
    }

}

//修改一下
@Component(modules = DaggerModule.class)
public interface DaggerComponnet {
    void inject(DaggerActivity daggerActivity);

    /**
     *这里是重点,需要什么类型的构造参数需要在这里定义,方法名称没有特殊限制,这里是链接构造参数的桥梁
     * @return
     */
    WrapDaggerData getDaggerGsonData();
}

运行项目


image.png

从结果可以看出来构造函数的值传递成功。

好了这篇文章就写到这里结束,这篇文章主要是介绍module和inject二种纬度的使用方法和区别以及构造函数如何传参的问题。

如有写的不对的地方敬请留言告知!

上一篇下一篇

猜你喜欢

热点阅读