Dagger2学习笔记

dagger2的缘由背景

dagger2除了也有一段时间了,但是现在各大开源项目都在使用它,虽然感觉他的用处并不是很大,但是看了几篇文章,现在写一篇文章来记录一下东西,方便以后查阅。先来看看dagger2的使用背景。

public class TestPresenter extends BasePresenter<TestView> {
   A  a;
   B  b;
   public TestPresenter(A a,B b) {
      this.b = b;
      this.a = a;
   }
}

那么我们在实例化这个Presenter的时候就需要这样做
A a = new A();
B b = new B();
需要把TestPresenter需要的参数一个个的实例化,然后作为参数传给它。试想一下,如果我们的A和B 还需要很多参数,或者TestPresenter需要3个乃至更多的参数,那我们实例化一个TestPresenter要做的操作是不是太多了,这样实在是太夸张了,我们只是需要一个TestPresenter实例而已,就必须知道TestPresenter的Dependency是什么,TestPresenter的Dependency的Dependency是什么。。。首先这样做操作很是繁琐,其次,这样做,导致代码耦合程度非常高,我们改变其中一环的时候,就要做大量的代码改动。
然而,如果我们使用dagger2,这些问题就可以迎刃而解了。dagger2用一个类似依赖工厂的东西,将所需的依赖统一管理起来,所有需要用到依赖的类都可以到这里寻找相应的依赖,并且dagger2会自动搜索这个类所用到的依赖的依赖,系统会自动识别这个依赖关系。

使用dagger2的好处

1.增加开发效率
2.更好地管理类实例
3.解耦

注解属性

先看看各个注解的所用属性,这里纯粹当做笔记看下就行。先了解个概念,后续不懂的时候可以在这里查看一下。
-Dagger2 通过注解来生成代码,定义不同的角色,主要的注解如下:

  • @Module: Module类里面的方法专门提供依赖,所以我们定义一个类,用@Module注解,这样Dagger在构造类的实例的时候,就知道从哪里去找到需要的依赖。
  • @Provides: 在Module中,我们定义的方法是用这个注解,以此来告诉Dagger我们想要构造对象并提供这些依赖。
  • @Inject: 通常在需要依赖的地方使用这个注解。换句话说,你用它告诉Dagger这个类或者字段需要依赖注入。这样,Dagger就会构造一个这个类的实例并满足他们的依赖。
  • @Component: Component从根本上来说就是一个注入器,也可以说是@Inject和@Module的桥梁,它的主要作用就是连接这两个部分。将Module中产生的依赖对象自动注入到需要依赖实例的Container中。
  • @Scope: Dagger2可以通过自定义注解限定注解作用域,来管理每个对象实例的生命周期。
  • @Qualifier: 当类的类型不足以鉴别一个依赖的时候,我们就可以使用这个注解标示。例如:在Android中,我们会需要不同类型的context,所以我们就可以定义 qualifier注解“@perApp”和“@perActivity”,这样当注入一个context的时候,我们就可以告诉 Dagger我们想要哪种类型的context。

Dagger2的使用步骤

1.导入方法:

  • 在整个项目的build.gradle中加入:
dependencies {
   // other classpath definitions here
   classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
}
  • 在app/build.gradle中分别加入:
// add after applying plugin: 'com.android.application'  apply plugin: 'com.neenbedankt.android-apt'
·  dependencies {
  // apt command comes from the android-apt plugin
  apt 'com.google.dagger:dagger-compiler:2.2'
  compile 'com.google.dagger:dagger:2.2'
  provided 'javax.annotation:jsr250-api:1.0'
}

2.注解的三个分类

  • 构造器注入@Inject标注在构造器上其实有两层意思。
    ①告诉Dagger2可以使用这个构造器构建对象。
    ②注入构造器所需要的参数的依赖。
    构造器注入的局限:如果有多个构造器,我们只能标注其中一个,无法标注多个。
  • 属性注入
    如MainActivity类,标注在属性上。被标注的属性不能使用private修饰,否则无法注入。
    属性注入也是Dagger2中使用最多的一个注入方式。
  • 方法注入
 public class MainActivity extends AppCompatActivity {

 private A a;

 @Inject
 public void setA(A a) {
     this.a= a;
 }
}

标注在public方法上,Dagger2会在构造器执行之后立即调用这个方法。
比如google mvp dagger2中,给View设置Presenter的时候可以这样使用方法注入。

其中构造器注入和属性注入一般是成对使用,而且一般使用方法基本上就是他们。方法注入一般用的比较少。在上面已经介绍过。

使用示例

先来看一个没有使用module提供类的,最简单的dagger2的是使用示例

 public class A{
@Inject
B b;
public A() {
}

}

public class B{

@Inject
public B() {
}

}
通过这样, 在A类中就初始化了变量B。

对于上面这种方式,我是可以在对应的目标类上是用inject来标注它的构造方法的,那当我们使用第三方的时候呢?这时候我们并不能修改他的方法,这就引入了module,module其实就是提供变量的一个管理类差不多,里面都是一些通过提供构造方法来获得变量的,然后通过component这个注入器将里边的变量注入到使用类中。一种图可以来简化他们的关系。

再来看一个使用了module提供目标类的使用,基于MVP模式的。用于初始化prsenter这个变量。

module类:

   @Module
public class AppModule {

TestView testView;
public AppModule(TestView testVIew){
  this.TestView = testView
}
    @Provides
    public TestPresenter providePresenter() {
        return new TestPresenter(testView);
    }
}

两个接口类:

   public interface TestView{//基于View接口层
        void showToast();
}
public interface BasePresenter{//Presnter的接口层
   void loadData();
}

看一下presenter的代码:

public class TestPresenter extends BasePresenter{

    TestView testView;

    public TestPresenter(TestView testView) {
        this.testView = testView;
    }

    @Override
    public void loadData(){
        ...
        testView.showToast();
    }
}

再来定义我们的Componment,定义Componment的两种方式:

@Component(modules = {AppModule.class})
public interface AppComponent {
    TestPresenter testPresenter();
}

@Component(modules = AppModule.class)
public interface AppComponent {
    TestActivity inject(TestActivity activity);
}

对于这种方式:Component显示暴露了调用方法,我们在activity中是这样初始化并使用的。

public TestActivity extend Activity{

    @Inject
    TestPresenter testPresenter;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        AppComponent appComponent =    DaggerAppComponent.builder().appModule(new AppModule(this)).build();
        mTestPresenter= appComponent.testPresenter();

    }

@Override
    public void showToast(){
       ......
    }

}

对于第二种方式,是这样使用的,通常情况下这种方式使用的概率会比较大。

public TestActivity extend Activity imlpments TestView{

    @Inject
    TestPresenter testPresenter;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        AppComponent appComponent =  DaggerAppComponent.builder().appModule(new AppModule(this)).build();
        appComponent.inject(this);

    }

    @Override
    public void showToast(){
       ......
    }
}

上面就是dagger2结合MVP模式的经典使用了,明白了基础方法的使用,可以看看dagger2的其它注解属性的使用。

@qualifier的作用

当目标类同时有两个构造函数的时候,而且同时被inject标注了,这时候就犯难了,使用类不知道使用哪一个。这时候qualifier的使用就来了。

public class Fruit{
  private String name;

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

  public Fruit(){
      name = "苹果";
  }
}

这里自定义一个qualifier注解

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface PersonQualifier {
}

FruitModule

@Module
public class FruitModule {

    //一个默认的
    @Provides
    Fruit provideFruit() {
        return new Fruit();
    }

    //采用@Qualifier注解,表示我可以提供这种标识符的Fruit 
    @Provides
    @Named("banana")
    Fruit provideFruit01() {
        return new Fruit("香蕉");
    }

    @Provides
    @Named("xueli")
    Fruit provideFruit02() {
        return new Fruit("雪梨");
    }

    @Provides
    @PersonQualifier
    Fruit provideFruitByQualifier() {
        return new Fruit("qualifier Fruit");
    }
}

然后我们在需要注解的Activity上看使用

@Inject
Fruit mFruit;

//这么多对象,如果需要特定的对象,用@Qualifier标识符注解,@Named是自定义的一个标识符注解
@Inject
@Named("banana")
Fruit mBanana;

@Inject
@Named("xueli")
Fruit mXueli;

@Inject
@FruitQualifier
Fruit mFruitQualifier;

@Scope和@Singleton

@Scope是用来管理依赖的生命周期的。它和@Qualifier一样是用来自定义注解的,而@Singleton则是@Scope的默认实现。

注意点:

@Scope是需要成对存在的,在Module的Provide方法中使用了@Scope,那么对应的Component中也必须使用@Scope注解

实际上@singleton并不是说标注了这个类就是单例类了。那要dagger2要实现单例。需要什么条件呢?

  • 依赖在Component中是单例的(供该依赖的provide方法和对应的Component类使用同一个Scope注解。)

  • 对应的Component在App中只初始化一次,每次注入依赖都使用这个Component对象。(在Application中创建该Component)

下面来看看实现@singleton的示例代码:

    @Module
public class ApplicationModule {
    private App mApplication;

    public ApplicationModule(App application) {
        mApplication = application;
    }

    @Provides
    @Singleton
    @ContextLife("Application")
    public Context provideApplicationContext() {
        return mApplication.getApplicationContext();
    }

}

ApplicationComponent.java

 @Singleton
@Component(modules = ApplicationModule.class)
public interface ApplicationComponent {
    @ContextLife("Application")
    Context getApplication();

}

// 单例的有效范围是整个Application

public class App extends Application {
    private static  ApplicationComponent mApplicationComponent; // 注意是静态
    public void onCreate() {
        mApplicationComponent = DaggerApplicationComponent.builder()
                .applicationModule(new ApplicationModule(this))
                .build();
    }

    // 对外提供ApplicationComponent
    public static ApplicationComponent getApplicationComponent() {
        return mApplicationComponent;
    }
}

当然也可以自定义@scope
可以看到定义一个Scope注解,必须添加以下三部分:

  • @Scope :注明是Scope
  • @Documented :标记文档提示
  • @Retention(RUNTIME) :运行时级别

对于Android,我们通常会定义一个针对整个APP全生命周期的@PerApp的Scope注解和针对一个Activity生命周期的@PerActivity和singletop的模式差不多,这里就不多说了。

组织component

  • 依赖:@Component的dependencies属性
  • 包含:@SubComponent注解
  • 继承:extends关键字

看这篇文章的例子:[点击传送](http://www.jianshu.com/p/47c7306b2994

总结

dagger2的工作原理

步骤1:查找Module中是否存在创建该类的方法。

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

只要明白这里面的原则,基本上就是dagger2的基本使用用法了。

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

推荐阅读更多精彩内容