Dagger2一步步实现MVP

前言

Dagger2是dagger的升级版,dagger是由square公司开发,github链接:https://github.com/square/dagger,不过,它的升级版dagger2,现在由Google接手维护,github链接:https://github.com/google/dagger
dagger2到底做什么用?简单地说:依赖注入。
在软件工程领域,依赖注入(Dependency Injection)是用于实现控制反转(Inversion of Control)的最常见的方式之一。

为什么需要依赖注入

控制反转用于解耦。
举个栗子

public class A{
  ...
  Class b;
  public A(){
      b=new B();
    }

public void doSomething(){
      b.do();
  }
  ...
}

上面的写法我们经常用到,也都是这么写的,那么这么写有什么问题吗?试想一下,如果Class B发生变化了,比如构造方法需要参数,或者do()方法需要传参数,那么我们就不得不改Class A的构造方法,所有用到A实例的地方就都需要修改了。这样,AB就仅仅耦合在一起了,对以后程序的扩展修改不利。开闭原则告诉我们程序应该是对外扩展开放,对修改关闭,这正好与这一原则相违背。

依赖注入的实现方式

我们的代码中有很多依赖注入,只不过我们没有察觉罢了。依赖注入主要有以下集中方法:

  • 构造函数注入
public class ClassH {

  ClassB classB;
  
  public ClassH(ClassB b){
    this.classB=b;
  }
}
  • setter方法注入
  public class ClassH {

  ClassB classB;
  
  public void setClassB(ClassB b){
    this.classB=b;
  }
}
  • 通过接口注入
interface IClassH(){
  void setB();
}

public class ClassH implement IClassH{

ClassB classB;

   @override
      void setB(ClassB b) {
          classB = b;
      }
}

最后,是通过Dagger2的方式注入。下面是通过http://www.jianshu.com/p/8527e3bb9097,MVP模式的登录栗子,用一下Dagger2。

Dagger2的引入
dependencies {
  compile 'com.google.dagger:dagger-android:2.x'
  compile 'com.google.dagger:dagger-android-support:2.x' // if you use the support libraries
  annotationProcessor 'com.google.dagger:dagger-android-processor:2.x'
annotationProcessor 'com.google.dagger:dagger-compiler:2.12'
}

对于Android Studio版本在2.2以下的,用以下的方法引入Dagger2。

  • 配置apt插件,在Project的build.gradle下
dependencies {
        classpath 'com.android.tools.build:gradle:2.3.1'
        classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
  • 添加依赖,在Module下的build.gradle下
apply plugin: 'com.android.application'
  //添加如下代码,应用apt插件
  apply plugin: 'com.neenbedankt.android-apt'
  ...
  dependencies {
      ...
      compile 'com.google.dagger:dagger:2.4'
      apt 'com.google.dagger:dagger-compiler:2.4'
      //java注解
      compile 'org.glassfish:javax.annotation:10.0-b28'
      ...
  }

Dagger2的使用

具体使用可以参照:https://google.github.io/dagger/android.html,Google的Dagger2官方支持,分Activity和Fragment的inject。
Dagger是基于MVP模式的,不了解MVP模式的请先移步 http://www.jianshu.com/p/8527e3bb9097,这是一个基于MVP模式的登录模块。

下面参考前面的mvp登录模块,一步步使用Dagger2。
以在LoginActivity中注入LoginPresenter为例,大致可以分为5步。

1 在LoginActivity中Inject目标类。

这里我们在LoginActivity中注入了LoginPresenter 的对象。mLoginPresenter,这样就可以不通过new的方式创建实例,达到了解耦的目的。

   @Inject
   LoginPresenter mLoginPresenter;

这里遇到了第一个注解符@Inject。

注解符

  • @Inject:在需要注入依赖的地方使用这个注解,你用它告诉Dagger这个 构造方法,成员变量或者函数方法需要依赖注入。这样,Dagger就会构造一个这个类的实例并满足他们的依赖。

注入之后,LoginActivity节持有了LoginPresenter的实例mLoginPresenter,如果这时候你运行一下代码,会报错,NullPointerException,为什么呢?原因是虽然我们Inject了mLoginPresenter,但是我们并没有真正实例化她,那么它在哪实例化呢?在Module里

2 编写Module,为注入时的实例化提供参数

先上代码

@Module
public class LoginModule {

    private IView.ILoginView loginView;

    public  LoginModule(IView.ILoginView view){
        this.loginView=view;
    }

    /**
     * 提供LoginPresenter
     * @param iLoginView
     * @return
     */
    @Provides
    @Singleton
    LoginPresenter provideLoginPresenter(IView.ILoginView iLoginView){
        return new LoginPresenter(iLoginView);
    }

    @Provides
    @Singleton
    IView.ILoginView provideILoginView(){
        return loginView;
    }
    
}

说明

  • Module的作用是用来提供生成依赖对象的,生成实例的,你可以把它看成是一个工厂,里面生产各种各样的实例。
  • 必须要用@Module注解,说明此类是个Module

注解符

  • @Module 用Module标注的类是专门用来提供所有依赖的,里面有几个provideXXX()方法就提供多少个依赖
  • @Provides 在Module中,我们定义的方法是用这个注解,以此来告诉Dagger我们想要构造对象并提供这些依赖。也就是说,@Provides用在方法上,是提供具体依赖的。
  • @Singleton 当前提供的对象将是单例模式 ,一般配合@Provides一起出现

至此我们的实例化过程完成了,下面还有一点要补充说明的

补充
是不是只能用Module提供以来对象呢,不是的,以我们的上面的例子说明,LoginActivity里注入了LoginPresenter对象,如果我们不编写Module,那怎么生成实例呢。

@Inject
   LoginPresenter mLoginPresenter;

答案是到LoginPresenter的构造方法前,加上@Inject注解。如下代码所示

@Inject
    public LoginPresenter(IView.ILoginView iLoginView) {
        this.mILoginView = iLoginView;
        mLoginModel = new LoginModel();
    }

这样也可以完成实例化过程。
也许你要问,既然这样可以完成实例化,为么还要编写Module呢?
答案是:

  • 如果项目中使用到了第三方的类库,第三方类库又不能修改,所以根本不可能把Inject注解加入这些类中,这时我们的Inject就失效了。
  • 如果我们以这种方法提供实例化,那么实例化过程会分散在整个项目中,而Module正好可以将它们集中起来管理。

整个注入,实例化的过程可以用下面的步骤描述:

当遇到@Inject注解时:

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

现在实例已经构建好了,现在我们有了(1)依赖的使用方:LoginActivity,通过 @Inject注入(2)依赖的提供方Module。
怎么才能在两者之间建立桥梁呢,答案是Component

3 编写Component

Component一般用来标注接口,如上文所说,作用在于作为依赖提供方和依赖使用方沟通的桥梁。

@Singleton
@Component(modules = {LoginModule.class})
public interface LoginComponent {

    void inject(LoginActivity loginActivity);

}
  • 一个类要想是Component,必须用Component注解,并且该类是接口或抽象类,并通过(modules = xxx.class)绑定对应module,并声明一个inject方法,在依赖的使用方会用到
  • @Inject和@Module的桥梁,它的主要作用就是连接这两个部分
  • 我们要列出所有的@Modules组成该组件,如果缺失了任何一块都会在编译的时候报错
4 编译。AndroidStudio -> Build -> Make Project

编译后,MainComponent生成一个DaggerXXXComponent类,这个类我们最后一步用到。

5 在Activity中注入
 DaggerLoginComponent.builder()
                .loginModule(new LoginModule(this))
                .build()
                .inject(this);

效果是一样的


代码下载:https://github.com/baojie0327/SsrjMvp

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

推荐阅读更多精彩内容