dagger2入门指南

dagger2入门指南

配置Dagger2

参考Dagger2 Wiki,在Gradle中添加以下依赖


dependencies {
  
      provided 'javax.annotation:javax.annotation-api:1.2'
  
      compile 'com.google.dagger:dagger:2.0.2'
  
      apt 'com.google.dagger:dagger-compiler:2.0.2'
}

apt是一个Gradle插件,协助Android Studio 处理Annotation Processors,所以在Gradle中还必须添加以下内容(关于Android-apt更多内容参考)


dependencies {
     
      classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
 
}

 apply plugin: 'com.neenbedankt.android-apt'

开始使用

Dagger2是实现依赖注入的一种手段,关于依赖注入,这里就不多介绍,想了解的可以参考, Dagger2中需要使用注解来完成依赖注入,我们来一个个介绍这些注解

@Inject

我们可以使用@Inject这个注解来标注目标类中所依赖的其他类,也可以用来标注所依赖的其他类的构造函数。举个例子

  
public class Human{
     
   @Inject 
   Father father;
  
}
  
  
public class Father{
     
    @Inject
     
    public Father(){  //如果标注有参数的构造方法,在调用构造方法前会去获取参数对应的对象
     
    }
  
}


Human需要通过@Inject注入Father这个类的时候,会先调用Father中标注的构造函数,生成相应的对象。这样HumanFather之间就有了一种无形的联系,但是仅仅这样还不够,我们还需要用到Component来使他们连接起来

@Component


@Component的作用就是连接目标类和目标类依赖实例(可以通过上面说的@Inject标注构造函数也可以通过@Module来产生)。那Component是怎么工作的呢?

  • 查找目标类中用@Inject标注的属性

  • 查找对应属性依赖实例

  • 将依赖实例进行赋值

目标类需要初始化自己依赖的其他类还需要调用Component的inject(Object object)方法开始注入

@Singleton @Component(modules = { ApplicationModule.class},dependencies={xxxComponent.class})
public interface ApplicationComponent {

  Context getContext();

  void inject(MyApplication mApplication);
}

比如在MyApplication中需要注入其他类,则需要在MyApplication中调用ApplicationComponentinject方法开始注入。

  • modules对应的就是提供依赖的Module类,可以有多个
  • dependencies表示该Component还依赖其他Component
  • @Singleton是一种Scope注解,下面会说到

@Module

介绍@Inject的时候我们说过,可以通过@Inject标注构造函数生成对应的对象,但是如果是第三方类库,我们无法添加@Inject注解的时候该怎么办,这时候就需要用到@Module了。举个列子

    
@Module
    
public class Module{
          
    //A是第三方类库中的一个类
          
    @Provides
 A provideA(){
               
           return A();
          
    }
    
}

  • 通过provideA()这个方法我们就可以生成一个A的实例,目标类中可以通过@Inject来注入

  • @provides标注的方法直接返回以创建或者新创建的对象,只在Module中使用

  • Component会首先从Module中查找需要注入的实例,如果找到了,则停止,如果没有找到则是继续从Inject标注的构造函数查找

@Qualifier

通过上面我们知道,创建类的实例有两种方式,这两个方式有先后之分,但是如果一个类在同一方式下有多个创建类的方法的时候,Component会选取哪个方法来创建这个类的实例呢?举个例子

    @Module
    public class Module{
          
          @Provides
          A provideA(){
            return A();
          }
          
          @provides
          A provideOtherA(){
                 return A();
          }
    }

如果我们按照上面这样写,Dagger2在编译的时候就会报错,那么我们改如何解决呢?这时候就需要用到@Qualifier了,我们可以用这个注解给不同的创建实例的方法进行标识并加以区分。

    @Module
    public class Module{
          
          @Named("firstA")
          @Provides
          A provideFirstA(){
               return A();
          }

          @Named("secondA")
          @provides
          A provideSecondA(){
                 return A();
          }
    }

@Named是Dagger2对于@Qualifier一个默认实现,我们也可以自定义,比如@ForApplication@ForAcitivity来标识不同的Context

@Qualifier
@Documented
@Retention(RUNTIME)
public @interface Named {

    /** The name. */
    String value() default "";
}

这样,通过@Named我们就可以区分不同的实例了

    public class Human{
       @Inject
       @Named("firstA")
       A firstA;

       @Named("secondA")
       @provides
       A secondA;
    }

@Scope

Scope是一个注解作用域,通过自定义注解限定对象的作用范围。通过这个注解能够解决不同对象生命周期不一致的问题,比如Application,ToastHelper等存在于应用的整个生命周期,Adapter,Presenter等则随着Activity的销毁而销毁。我们可以通过自定义不同的Scope来区分。举个例子

@Scope
@Documented
@Retention(RUNTIME)
public @interface PerApp{}
@PerApp @Component(modules = { ApplicationModule.class})
public interface ApplicationComponent {

  Context getContext();

  void inject(MyApplication mApplication);
}
@Module 
public class ApplicationModule {

  private final Context context;

  public ApplicationModule(Context context) {
    this.context = context;
  }

  @Provides @PerApp public Context provideApplicationContext() {
    return context.getApplicationContext();
  }
}

我们在ApplicationModule中定义了创建Context实例的方法,在ApplicationComponent中管理ApplicationModule,因为ApplicationComponent只有在Application中实例化一次,所以Context的生命周期也就和Application一样了,也就是Dagger并不会帮你管理生命周期,只能自己来控制。那么PerApp这个注解有什么用呢?

  • 更好的管理ComponentModule之间的匹配关系,Dagger2在编译的时候会检查Component管理的Module,若发现Component所标注的自定义Scope注解与Module中不一样,就会报错。
  • 提高可读性,通过PerApp就知道是在整个Application有小范围内

注意点:

  • 一个Module中只能存在一种Scope
  • Scope标注的Component和所依赖的ComponentScope不能一样

实例讲解

创建ApplicationComponent

@PerApp
@Component(modules = { ApplicationModule.class })
public interface ApplicationComponent {
  Context getContext();

  ToastUtils getToastUtils();
}

ApplicationComponent是一个全局的Component,负责管理整个App的全局类实例,Context getContext();ToastUtils getToastUtils();是将ContextToastUtils暴露给子Component。

创建ApplicationModule

@Module 
public class ApplicationModule {
  private final MyApplication myApplication;

  public ApplicationModule(MyApplication myApplication) {
    this.myApplication = myApplication;
  }

  @PerApp @Provides Context provideContext() {
    return myApplication.getApplicationContext();
  }

  @PerApp @Provides ToastUtils provideToastUtils(Context mContext) {
    return new ToastUtils(mContext);
  }
}

ApplicationModule生成全局ContextToastUtils实例

在Application中创建ApplicationComponent实例

public class MyApplication extends Application {
  private ApplicationComponent mApplicationComponent;

  @Override public void onCreate() {
    super.onCreate();
    mApplicationComponent =
        DaggerApplicationComponent.builder().applicationModule(new ApplicationModule(this)).build();
  }

  public ApplicationComponent getApplicationComponent() {
    return mApplicationComponent;
  }
}

DaggerApplicationComponent这个是有Dagger2生成的类,因为ApplicationComponent只初始化一次,所以注入的类都是单例的,这就是Dagger2真正创建单例的方法。

划分Component

一个应用应该包含一个全局的Component,负责管理整个全局类的实例,其他Componet建议按照页面来划分。

@PerActivity 
@Component(dependencies = ApplicationComponent.class, modules = {
    MainModule.class, ActivityModule.class
}) 
public interface MainComponent {

  void inject(MainActivity mActivity);
}
@Module
public class MainModule {

  @PerActivity @Provides GetUser getUser() {
    return new GetUser();
  }
}
@PerActivity
@Component(modules = { ActivityModule.class })
public interface ActivityComponent {

  Activity getActivity();

}
@Module 
public class ActivityModule {
  private final Activity mActivity;

  public ActivityModule(Activity mActivity) {
    this.mActivity = mActivity;
  }

  @Provides @PerActivity Activity provideActivity() {
    return mActivity;
  }
}

上面是Main这个页面的划分

DEMO

demo

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

推荐阅读更多精彩内容