Rx系列<第三十四篇>:Android开发需要了解的Dagger2

在Android中Dagger2主要目的是解耦,一般结合MVP达到完全解耦的效果。

(1)什么是Dagger2?

Dagger是为Android和Java平台提供的一个完全静态的,在编译时进行依赖注入的框架,原来是由Square公司维护,现在由Google维护。

(2)存在的意义

我们在activity中有可能会用到很多很多的类,这些类要在activity中进行实例化,这样就导致我们的activity非常依赖这么多的类,这样的程序耦合非常严重,不便于维护和扩展,有什么办法可以不去依赖这些类呢,这时候就需要有一个容器,将这些类放到这个容器里并实例化,我们activity在用到的时候去容器里面取就可以了,我们从依赖类到依赖这个容器,实现了解耦,这就是我所理解的依赖注入,即所谓控制反转。

(3)注解之@Inject

主要有两个作用,一个是使用在构造函数上,通过标记构造函数让Dagger2来使用(Dagger2通过Inject标记可以在需要这个类实 例的时候来找到这个构造函数并把相关实例new出来)从而提供依赖,另一个作用就是标记在需要依赖的变量让Dagger2为其提供依赖。

(4)注解之@Module

用Module标注的类是专门用来提供依赖的。有的人可能有些疑惑,看了上面的@Inject,需要在构造函数上标记才能提供依赖,那么如果我们需要提供的类构造函数无法修改怎么办,比如一些jar包里的类,我们无法修改源码。这时候就需要使用Module了。Module可以给不能修改源码的类提供依赖,当然,能用Inject标注的通过Module也可以提供依赖

(5)注解之@Provides

用Provide来标注一个方法,该方法可以在需要提供依赖时被调用,从而把预先提供好的对象当做依赖给标注了@Inject的变量赋值。provide主要用于标注Module里的方法

(6)注解之@Component

Component一般用来标注接口,被标注了Component的接口在编译时会产生相应的类的实例来作为提供依赖方和需要依赖方之间的桥梁,把相关依赖注入到其中。

(7)添加依赖
implementation 'com.google.dagger:dagger:2.22.1'
annotationProcessor 'com.google.dagger:dagger-compiler:2.22.1'
(8)@Inject和@Component使用

我们先看一下未注入Dagger的代码

public class MainActivity extends BaseActivity {

    private DaggerTest daggerTest;

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

        daggerTest = new DaggerTest();

        findViewById(R.id.bt_1).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                daggerTest.print();
            }
        });

    }
}


public class DaggerTest {

    /**
     * 打印测试数据
     */
    public void print(){
        Log.d("aaa", "做一个小测试");
    }
}

在MainActivity中创建一个DaggerTest对象,这样的话MainActivity和DaggerTest就产生了依赖关系,这就产生了耦合。下面将使用Dagger来解耦:

第一步 在MainActivity中使用@Inject注解

public class MainActivity extends BaseActivity {

    @Inject
    DaggerTest daggerTest;

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

        findViewById(R.id.bt_1).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                daggerTest.print();
            }
        });

    }
}

第二步 在DaggerTest的构造方法中使用@Inject注解(构造方法不可以是private)

public class DaggerTest {

    @Inject
    public DaggerTest(){

    }

    /**
     * 打印测试数据
     */
    public void print(){
        Log.d("aaa", "做一个小测试");
    }

}

第三步 新建一个TestComponent接口,并用@Component修饰

Component是连接注入类和目标类的桥梁。

@Component
public interface TestComponent {

    void inject(MainActivity mainActivity);

}

Component会查找目标类中用Inject注解标注的属性,查找到相应的属性后会接着查找该属性对应的用Inject标注的构造函数

第四步 将目标类注入到Component中

我们在MainActivity中的onCreate方法中添加以下代码

    DaggerTestComponent.create().inject(this);

这种方式的局限性:

  • DaggerTest构造方法不可以带有参数;
  • DaggerTest不可以存在多个构造方法;
(9)解决第(8)项构造方法中不能带参数的问题

要想构造方法中可以带有参数,需要@Module和@Provides的支持。

第一步 在MainActivity中使用@Inject注解

public class MainActivity extends BaseActivity {

    @Inject
    DaggerTest daggerTest;

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

        findViewById(R.id.bt_1).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                daggerTest.print();
            }
        });

    }
}

第二步 在DaggerTest的构造方法中使用@Inject注解

public class DaggerTest {

    @Inject
    public DaggerTest(String s){

    }

    /**
     * 打印测试数据
     */
    public void print(){
        Log.d("aaa", "做一个小测试");
    }

}

第三步 新建Model

@Module
public class TestModel {

    @Provides
    public DaggerTest provideDaggerTest(){
        return new DaggerTest("a");
    }

}

Model类使用@Module修饰,@Provides修饰任意方法并提供对象的实例。

第四步 新建一个TestComponent接口,用@Component修饰,并指定某Model

@Component(modules = TestModel.class)
public interface TestComponent {

    void inject(MainActivity mainActivity);

}

第五步 将目标类注入到Component中

我们在MainActivity中的onCreate方法中添加以下代码

    DaggerTestComponent.builder()
            .testModel(new TestModel())
            .build()
            .inject(this);

当然TestModel的构造方法可以带有参数

    DaggerTestComponent.builder()
            .testModel(new TestModel("我传递了一个数据"))
            .build()
            .inject(this);

这种方式的局限性:

  • DaggerTest不可以存在多个构造方法;
(10)注入类拥有多个构造方法的情况

使用@Named可以使构造方法分类,我就不分步骤了,直接贴一下代码:

public class MainActivity extends BaseActivity {

    @Named("type1")
    @Inject
    DaggerTest daggerTest1;

    @Named("type2")
    @Inject
    DaggerTest daggerTest2;

    @Named("type3")
    @Inject
    DaggerTest daggerTest3;

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

        DaggerTestComponent.builder()
                .testModel(new TestModel("我传递了一个数据"))
                .build()
                .inject(this);


        findViewById(R.id.bt_1).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                daggerTest1.print();
                daggerTest2.print();
                daggerTest3.print();
            }
        });

    }
}


public class DaggerTest {

    private String printStr = "";

    public DaggerTest(){

    }

    public DaggerTest(String s){
        printStr = s;
    }

    public DaggerTest(String s, String ss){
        printStr = s + ss;
    }

    /**
     * 打印测试数据
     */
    public void print(){
        if(!TextUtils.isEmpty(printStr)){
            Log.d("aaa", printStr);
        }else{
            Log.d("aaa", "做一个小测试");
        }
    }
}


@Module
public class TestModel {

    private String test;

    public TestModel(String s){
        test = s;
    }

    @Named("type1")
    @Provides
    public DaggerTest provideDaggerTest1(){
        return new DaggerTest();
    }

    @Named("type2")
    @Provides
    public DaggerTest provideDaggerTest2(){
        return new DaggerTest(test);
    }

    @Named("type3")
    @Provides
    public DaggerTest provideDaggerTest3(){
        return new DaggerTest(test, "1");
    }
}


@Component(modules = TestModel.class)
public interface TestComponent {

    void inject(MainActivity mainActivity);

}
(11)自定义注解替代@Named

@Qualifier注解可以解决注入类拥有多个构造方法时依赖冲突的问题,下面我们自定义一个注解@CustomName

@Qualifier
@Documented
@Target({ ElementType.FIELD, ElementType.METHOD })
@Retention(RUNTIME)
public @interface CustomName {

    String value() default "";

}

我们只要将(10)中的@Named换成@CustomName即可。

(12)单例模式

使用@Singleton注解实现单例。

第一步 在Component中添加@Singleton

@Singleton
@Component(modules = TestModel.class)
public interface TestComponent {

    void inject(MainActivity mainActivity);

}

第二步 在Model中的被@Provides修饰的方法上添加@Singleton

@Singleton
@Provides
public DaggerTest provideDaggerTest1(){
    return new DaggerTest();
}

以上两部可以实现局部单例。

当我们从各种Activity中都使用了以下代码

    DaggerTestComponent.builder()
            .testModel(new TestModel("我传递了一个数据"))
            .build()
            .inject(this);

那么DaggerTest只在各个Activity中是单例的,不同Activity中的DaggerTest的hashcode不同。现在我们将局部单例改造成全局单例

  • 自定义Application
public class MyApp extends Application {

    private static TestComponent testComponent;

    @Override
    public void onCreate() {
        super.onCreate();
        testComponent = DaggerTestComponent.builder()
                .testModel(new TestModel("我传递了一个数据"))
                .build();
    }

    public static TestComponent getTestComponent() {
        return testComponent;
    }

}

public static TestComponent getTestComponent() {
    return testComponent;
}
  • 在Activity引用Activity中的Component对象

      MyApp.getTest().inject(this);
    
    (13)自定义Scope

自定义Scope比较简单,现在我们想定义一个@MyScope注解,代码如下

@Scope
@Documented
@Retention(RUNTIME)
public @interface MyScope {

}

@Singleton则是@Scope的默认实现,所以也可以写成

@Scope
@Documented
@Retention(RUNTIME)
public @interface Singleton{

}
(14)延迟注入(Lazy)

有时我们想注入的依赖在使用时再完成初始化,加快加载速度,就可以使用注入Lazy<T>。只有在调用 Lazy<T>get() 方法时才会初始化依赖实例注入依赖。

@Inject
Lazy<DaggerTest> daggerTest1;


daggerTest1.get().print(String.valueOf(daggerTest1.hashCode()));
(15)多个依赖的情况

假设有A,B两个类,在A中新建一个B对象,那么A和B就产生了依赖,我们称之为:A依赖于B

结合上面讲解到的知识,我们很容易就会想到如何使用Dagger来解耦:

第一步 在A中使用@Inject修饰B的成员变量

@Inject
A a;

第二步 使用@Module@Provides新建一个Model

@Module
public class BModel {

    @Provides
    public DaggerTest provideDaggerB(){
        return new B();
    }

} 

第三步 使用@Component定义接口,这个接口作为A和B形成依赖关系的桥梁

@Component(modules = BModel.class)
public interface ABComponent {

    void inject(A a);

}

第四步 在A中使用以下代码,即可完成A和B的依赖关系。

   ABComponent abComponent = DaggerABComponent.builder()
            .testModel(new BModel())
            .build()
            .inject(this);

以上四个步骤比较简单,但是这时又来了一个C类,在C类中新建一个B对象,那么C和B就产生了依赖,我们称之为C依赖于B

我们可以在@Component新增接口可以实现

@Component(modules = BModel.class)
public interface ABComponent {

    void inject(A a);

    void inject(C c);

}
(16)一个桥梁可以引用多个modules
@Component(modules = {TestModel.class, Test2Model.class})
public interface TestComponent {

    void inject(MainActivity mainActivity);

}
(17)Component的依赖关系

Component就是桥梁,如果某桥梁想要使用另一个桥梁的方法时,可通过依赖实现。

第一步 使用@Inject声明成员变量

@Inject
DaggerTest2 daggerTest2;

第二步 新建Modules

@Module
public class Test2Model {

    @Provides
    public DaggerTest2 provideDaggerTest1(){
        return new DaggerTest2();
    }
}

第三步 桥梁

@Component(modules = {TestModel.class})
public interface TestComponent {

    void inject(MainActivity mainActivity);

    DaggerTest getDaggerTest();

}


@Component(modules = Test2Model.class, dependencies = TestComponent.class)
public interface Test2Component {

    void test(DemoActivity mainActivity);

}

第四步 产生依赖,并调用依赖桥梁的方法

    DaggerTest daggerTest = DaggerTestComponent.create().getDaggerTest();
    daggerTest.print("aaaa");

    DaggerTest2Component.builder()
            .testComponent(DaggerTestComponent.create())
            .test2Model(new Test2Model())
            .build()
            .test(this);
(18)可参考的其它博客

Dagger 2 完全解析(一),Dagger 2 的基本使用与原理
Dagger 2 完全解析(二),进阶使用 Lazy、Qualifier、Scope 等
Dagger 2 完全解析(三),Component 的组织关系与 SubComponent
Dagger 2 完全解析(四),Android 中使用 Dagger 2
Dagger 2 完全解析(五),Kotlin 中使用 Dagger 2
Dagger 2 完全解析(六),dagger.android 扩展库的使用

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

推荐阅读更多精彩内容

  • Dagger2 转载请注明原作者,如果你觉得这篇文章对你有帮助或启发,可以关注打赏。 前言本文翻译自Google ...
    轻云时解被占用了阅读 6,640评论 4 31
  • 前言   Dagger是帮助实现依赖注入的库,虽然很多人都知道依赖注入对于架构设计的重要性,但是Dagger学习曲...
    申国骏阅读 2,726评论 5 9
  • 原文地址Dagger2 入门,以初学者角度 依赖注入 Dagger2是Android中比较热门的依赖注入框架,什么...
    Marlon_IT阅读 1,268评论 1 16
  • 文章翻译自Dagger官网,翻译水平有限,见谅。 引入 引用官网的引入说明,上面的部分都好理解,就是简单的comp...
    one_cup阅读 616评论 0 0
  • 雨, 零落一夜寒意 牵手夜风徐徐, 偷跑进恋人的外衣, 肆意妄为深秋的情绪, 阻隔恋人相依。 看他们各自的士, 慌...
    罗子阅读 1,036评论 60 96