Android Dagger2简单使用

Dagger是一个很古老的框架了,当初诞生时候,主要是为了模块之间的解耦。本篇文章主要介绍一下如何使用dagger2,后续会介绍其原理。

AS集成

对于现在的AS项目,一般都是会集成Kotlin和Java混写,所以可以在想要使用dagger的模块module的gradle下加入如下配置。

java 复制代码
implementation 'com.google.dagger:dagger:2.21'
kapt 'com.google.dagger:dagger-compiler:2.21'

注意这里用到了kapt,这就要求我们对应module的gradle顶部有如下配置:

arduino 复制代码
    apply plugin: 'kotlin-kapt'

如何使用

假设我们现在存在一个类TestA, 结构如下:

csharp 复制代码
    public class TestA {

        private int count = 0;
        
        public String getValue() {
            return "123321";
        }
        
        public int addSelf(){
            return ++count;
        }
    }

我们现在呢,要在TestB中使用到TestA的对象实例,常规写法呢,就是new一个对象进行方法调用:

  • 常规写法
csharp 复制代码
    public class TestB {

        private final TestA testA = new TestA();

        public void show() {
            for (int i = 0; i < 2; i++) {
                testA.addSelf();
                System.out.println(testA.getValue());
            }
        }
    }
  • 使用dagger

常规写法要求我们对于一个对象必须要实例化,才能使用。那么在项目比较大型的时候,如果很多地方都用到了该对象,且进行了实例化。那么后续只要对象构造器里增加一个入参,那么就需要修改所有地方。

这样很容易出错,dagger这个时候就可以起作用了,dagger可以帮我们自动去书写实例化对象的代码,而我们只需要关注使用testA对象即可。

让我们看下使用dagger,这里代码怎么写。

csharp 复制代码
    public class TestB {

        @Inject
        TestA testA;

        public void show() {
            for (int i = 0; i < 2; i++) {
                testA.addSelf();
                System.out.println(testA.getValue());
            }
        }
    }

可以看到,区别就是将new TestA去掉了,加上了@Inject注解,让Dagger自动帮我们去进行TestA的实例化,我们只需要加上注解后,在下方直接使用其方法即可。

现在我们知道了dagger的作用和简单的使用场景,我们来看下如果要实现上述例子中TestA这种直接加注解来实例对象的方式,该怎么操作。

使用dagger来实现TestA自动实例化

下图是Dagger里最基本的几个注解的使用及关系。

对于上述TestA的例子。我们首先按照上图所示,定义一个Module,如下:

typescript 复制代码
    @Module
    public class TestAModule {

        @Provides
        public TestA provide() {
            return new TestA();
        }
    }

这里对于provide方法可以任意命名,只要返回值是TestA就可以。同时TestAModule也可以任意命名,只要加上Module注解即可。

接着,我们定义桥梁Component,类和方法也可以随意命名。

java 复制代码
    @Component(modules = TestAModule.class)
    public interface TestAComponent {

        void injectIntoTestB(TestB testB);
    }

注意Component上要写上,该Component所对应的Module,以便Dagger可以关联两者。同时内部定义一个接口也需要传入TestB对象,以便告诉Dagger,我们想要在哪个类中使用TestA实例。

做完这些,最后一步就是在TestB中使用了。

csharp 复制代码
    public class TestB {

        @Inject
        TestA testA;

        public void show() {
            for (int i = 0; i < 2; i++) {
                testA.addSelf();
                System.out.println(testA.getValue());
            }
        }
    }

注意这里TestA不可以是private修饰。

我们需要先build一下,编译完成后,还需要最后一步,在TestB中运行一下Dagger。可以在show方法中加上如下方法:

kotlin 复制代码
DaggerTestAComponent.create().injectIntoTestB(this);

DaggerTestAComponent是Dagger在我们构建编译时候为我们生成的,所以此时不编译是看不到这个类的,必须编译一下。

运行一下,我们就可以使用这个类了。所有Dagger自动生成的类都是以Dagger开头。

<math xmlns="http://www.w3.org/1998/Math/MathML"> 如果找不到文件!! \color{red}{如果找不到文件!!} </math>如果找不到文件!!

可能某些小伙伴发现,编译后仍然找不到这个类,那么说明你的项目开启了增量编译,需要gradle.properties中将其关闭。     在 gradle.properties 中加上如下配置,然后删除build文件夹,重新编译。

ini 复制代码
kapt.incremental.apt = false

最后我们运行一下,发现日志可以成功打印,说明执行成功了。

注意这个时候,TestB文件中张这样。

csharp 复制代码
public class TestB {

    @Inject
    TestA testA;

    public void show() {
        DaggerTestAComponent.create().injectIntoTestB(this);
        for (int i = 0; i < 2; i++) {
            testA.addSelf();
            System.out.println(testA.getValue());
        }
    }

实现单例

上述我们简单实现了个例子,同时我们可以在类中直接实例化一个对象并进行使用。我们看下如何实现。

我们可以在Provides注解标注的方法上加上Singleton注解,表示通过该方法生成的对象为单例对象,但是要注意,该单例的范围仅限于inject时候传入的对象。

比如:

less 复制代码
@Module
public class TestAModule {

    @Provides
    @Singleton
    public TestA provide() {
        return new TestA();
    }
}

注意在Component上也要加上Singleton注解。

less 复制代码
@Component(modules = TestAModule.class)
@Singleton
public interface TestAComponent {

    void injectIntoTestB(TestB testB);

    void injectIntoTestC(TestC testC);
}

对于上述方法,我在TestB和TestC中使用时候:

kotlin 复制代码
public class TestB {

    @Inject
    TestA testA1;

    @Inject
    TestA testA2;

    public void show() {
        DaggerTestAComponent.create().injectIntoTestB(this);
        System.out.println(testA1);
        System.out.println(testA2);
    }
}

public class TestC {

    @Inject
    TestA testA1;
    
    @Inject
    TestA testA2;

    public void dd() {
        DaggerTestAComponent.create().injectIntoTestC(this);
        System.out.println("C:" + testA1);
        System.out.println("C:" + testA2);
    }
}

我们同时运行dd和show方法时候,会发现两个dd方法内地址打印一样,show方法内地址打印一样,但是dd方法和show方法打印的地址就不一样了。这是因为单例的范围和inject传入的对象有关,只在传入的对象范围内生效。

name标签

Dagger存在一个Name标签,主要用于确定对象的不同生成方式。比如我们TestA存在多个构造器。

csharp 复制代码
public class TestA {

    public TestA() {
    }

    public TestA(int count) {
        this.count = count;
    }

    private int count = 0;

    public String getValue() {
        return "123321 " + count;
    }

那么可以在module中使用name来区分不同方式来生成对象实例。

typescript 复制代码
@Module
public class TestAModule {
    @Provides
    @Named("11")
    public TestA provide() {
        return new TestA();
    }

    @Provides
    @Named("22")
    public TestA provide2() {
        return new TestA(22);
    }

我们在TestB中使用,

less 复制代码
public class TestB {

    @Inject
    @Named("11")
    TestA testA1;

    @Inject
    @Named("22")
    TestA testA2;

    public void show() {
        DaggerTestAComponent.create().injectIntoTestB(this);
        System.out.println("11=>" + testA1.getValue());
        System.out.println("22=>" + testA2.getValue());
    }

运行一下方法 show,可以看到此时testA1和testA2分别调用了不同的构造器生成了对象。

总结

个人感觉,dagger在当下直接使用,比较鸡肋,想不到很合适的使用场景,毕竟当下这么多框架和新技术,对于各种场景,完全有更合适的框架。所以本文只打算对dagger研究这么多。为后续Hilt学习打个基础吧。

相关推荐
cding8 个月前
Dagger2的使用
android·dagger