煎饼

煎饼为你分享Android有关的技术文章
不断分享,点滴积累,共同提高

关注微信公众号[developers]
更快的了解新的技术动态

Dagger on Android-Dagger2详解

Dagger on Android-基础篇

Dagger on Android-Dagger1介绍

Dagger on Android-从Dagger1到Dagger2

以上三篇文章是前一段时间整理的,从本篇开始,就来跟我一起学习如何使用Dagger2吧。如果你迫不及待地想了解Dagger2的使用,那你可以先去看一下在Dagger2 Snapshot 版本发布后,我在CSDN上发的 Android 依赖注入: Dagger 2 实例讲解(一).

下面就跟我一起一步一步来学习 Dagger2.

一、配置Gradle

参考GitHub上 Dagger2 Wiki中的介绍,将Maven格式的配置文件转化成Gradle格式,如下:

1
2
3
4
dependencies {
    compile 'com.google.dagger:dagger:2.0.2'
    provided 'com.google.dagger:dagger-compiler:2.0.2'
}

提示:provided 表示仅在编译时用到。Dagger-compiler会在编译时生成相关代码

添加Dagger2的库之后,这还没有完成,还有两点需要注意:

  • 1、Dagger2 依赖Java Annotation Processors 来分析和验证依赖。Android Studio默认不会识别Dagger2自动生成的代码,使用 android-apt plugin可以将这些生成的文件添加到IDE类路径中去,而且还可以让你Trace到这些代码。
dependencies {
     // other classpath definitions here
     classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
 }
apply plugin: 'com.neenbedankt.android-apt'
  • 2、Dagger2 生成的代码中会使用@Generated注解,而javax.anotation.generated在Java 6及以上版本的JDK中都有,在Android API中是没有的,所在在Android项目中要加入以下库的依赖:
provided 'org.glassfish:javax.annotation:10.0-b28'

如果不添加这个,在编译时会报以下错误:

1
2
error: cannot find symbol class Generated
error: package javax.annotation does not exist

配置完成后的build.gradle文件如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
apply plugin: 'com.android.application'
apply plugin: 'com.neenbedankt.android-apt'

android {
    compileSdkVersion 23
    buildToolsVersion "23.0.2"

    defaultConfig {
        applicationId "com.fidroid.dagger2example"
        minSdkVersion 14
        targetSdkVersion 23
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'),
             'proguard-rules.pro'
        }
    }
}

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        // replace with the current version of the Android plugin
        classpath 'com.android.tools.build:gradle:2.0.0-alpha5'
        // replace with the current version of the android-apt plugin
        classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:23.1.1'

    //Dagger
    compile 'com.google.dagger:dagger:2.0.2'
    provided 'com.google.dagger:dagger-compiler:2.0.2'
    provided 'org.glassfish:javax.annotation:10.0-b28'
}

配置完成之后,点击 这个按钮就可以引入Dagger2的库了。至此使用Dagger2的准备工作已经完成。

二、开始使用

1、相关Annotation说明

在前面的文章中讲解Dagger1时已经已经介绍过,由于Dagger2又加入了一些新的Annotation,这里在统一介绍一下:

@Module: 表明这个类是一个Dagger module,类中的方法提供依赖。

injects: 表明这个module所要注入的类,我们还说明这类想会被直接注入ObjectGraph,这点下面会说。

@Provides: 表明这个方法是注入的提供者(injection provider).方法的名称是什么不重要,关键是返回的类型。在@Module注解的类中使用。

@Singleton: 使用了Singleton注解的方法总是会返回相同的实例化对象,这种方式比平时用的单例模式要好很多。每次注入,我们不会得到一个新的实例对象,而是返回一个已经存在,同时也解释了provider的作用。Application 对象是唯一的。

@Component: 其注解的类是modules与injection之间的桥接接口(interface),有自己的生命周期,生命周期结束,接口下提供的对象也会被回收。

@Subcomponet: 用来扩展@Component,同Component基本有一样的属性。不同的是:

  • 1、其注解的接口要在父Component中声明
  • 2、可以访问父Component中的所有元素

@Scope: 这个注解使Dagger持有Component中提供的对象的一个实例,这个实例随component有一定的生命周期,Provider 方法如果没有使用@Scope注解,它提供的对象在每次使用@Inject注入使用时都会新建一个新的对象。@Singleton是一个被Dagger定义的特殊的Scope,我们也可以定义自己的Scope。(之所以在介绍Dagger1中没有介绍Scope这个概念,一是因为刚接触Dagger的同学开始学习时本来就搞不清楚,如果再加入Scope恐怕会更难理解,之前的介绍只是为了入门。二是在Dagger1中实现这个相对Dagger2会复杂一点,也不容易懂,而且在使用中比较少用。)另外,一旦同时使用自己定义的@Scope与@Singleton,那后者将会失效。

另外需要注意的是:使用@Scope注解的Component不能被没有使用其注解的Component所依赖。[注1]

2、创建Module

这里我们创建两个Module类:AppModule.java 和 DomainModule.java。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
@Module
public class AppModule {

    private MyApplication mApplication;

    public AppModule(MyApplication application) {
        mApplication = application;
    }

    @Provides
    @Singleton
    Application providesApplication() {
        return mApplication;
    }

    @Provides
    @Singleton
    Context providesContext() {
        return mApplication;
    }
}
1
2
3
4
5
6
7
8
@Module
public class DomainModule {
    @Provides
    @Singleton
    public AnalyticsManager provideAnalyticsManager(Context context) {
        return new AnalyticsManager(context);
    }
}

这个AppModule同使用Dagger1时基本没有差别,就是缺省了@Module的参数,DomainModule中只有一个提供AnalyticsManager对象的方法,这个大家应该都很熟悉了,这里就不再赘述。可对比Dagger on Android-Dagger1介绍

3、创建AppComponent

@Component是Dagger2中的一个新的注解,它提供了连接Module和Injection的接口。这里创建AppComponent如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
@Component(
        modules = {
                AppModule.class,
                DomainModule.class
        }
)
@Singleton
public interface AppComponent {
    MyApplication inject(MyApplication app);
    //获取其他对象的方法
    ....
}

@Component中包含modulesdependencies两个参数,这个modules和 Dagger1中的includes作用基本是一样,dependencies指向所依赖的外部类,下面使用时再具体说明。

AppComponent是一个接口(interface),包含了一个inject(MyApplication app)方法,这个方法不是必须的,原因在下面会详细说明。

@Component的作用是为Module和Injection提供接口,所以我们获取其他所依赖的对象,也要在这里添加相应的方法。不管现在你有没有明白这句话,这个问题将在下面重点说明,请继续往下看。

4、使用Dagger2

Module 和 Component都已经完成,下一步就要进行编译,使Dagger2在编译时生成代码。

编译:shitf+R 活着使用gradle build再活着鼠标点击运行按钮. <😊Android Studio 2.0 Preview 5 Instant Run 真是太好用了😊>

编译之后会生成一些代码,稍后会进行分析。

a、创建MyApplication并完成Dagger的初始化:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
public class MyApplication extends Application {
    AppComponent appComponent = null;
    @Inject
    AnalyticsManager analyticsManager;

    @Override
    public void onCreate() {
        super.onCreate();
        appComponent = DaggerAppComponent.builder()
                .appModule(new AppModule(this))
                .domainModule(new DomainModule())
                .build();
        appComponent.inject(this);
        analyticsManager.register();
        analyticsManager.send("start ");
    }

    public AppComponent getAppComponent() {
        return appComponent;
    }
}

DaggerAppComponent使Dagger自动生成的,是AppComponent的实例。

appComponent.inject(this) 这句是将Dagger注入到MyApplication这个类中,在这句之后,我们可以通过注入其它对象完成相应功能,这里注入了AnalyticsManager对象。如果MyApplication中并不需要依赖其他对象,那么Dagger就不必在此注入,也就是说AppComponent就不用提供 inject(MyApplication app) 这个接口,可以提供一个MainActivity inject(MainActivity activity) 这样的接口,直接在Activity中注入(这解释了上面的相应问题),但是,在实际应用中,像上面代码一样,这个接口还是需要的。

b、创建一个ActiviyComponent

1
2
3
4
5
6
7
8
@Component(
        dependencies = AppComponent.class
)
@ScopeActivity
public interface ActivityComponent {
    void inject(MainActivity activity);
    OSHelper getOSHelper();
}

dependencies = AppComponent.class 表明这个Component依赖于AppComponent.

这里看到有个 @ScopeActivity 注解,这个不是Dagger自带的,是自定义的。因为AppComponent使用@Singleton注解,上面说过@Singletion是一个特殊的@Scope,所以ActivityComponent如果没有使用 @Scope 注解,那么将无法依赖 AppComponent. 看上面[注1]

自定义ScopeActivity:

1
2
3
4
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface ScopeActivity {
}

ActivityComponent 提供了两个方法:

inject(MainActivity activity) 方法将Component注入到MainActivity

getOSHelper() 方法获取一个OSHelper的对象,这个对象随MainActivity有一样的生命周期

OSHelper代码如下:

1
2
3
4
5
6
7
8
9
public class OSHelper {
    @Inject
    public OSHelper() {  //inject 注解的构造函数是必要的
    }

    public String getDeviceBrand() {
        return Build.BRAND;
    }
}

下面将Dagger注入到MainActivity,并调用OSHelper中的方法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";
//    @Inject
    OSHelper osHelper;

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

        //获得ActivityComponent对象
        ActivityComponent activityComponent = DaggerActivityComponent.builder()
                .appComponent(((MyApplication) getApplication()).getAppComponent())
                .build();

        //注入到MainActivity中
        activityComponent.inject(this);

        //通过ActivityComponent中的依赖对象获取接口获得OSHelper对象
        osHelper = activityComponent.getOSHelper();
        Log.d(TAG, "onCreate: " + osHelper.getDeviceBrand());
    }
}

至此,Daggger的使用已经讲解完毕,使用起来还是挺方便的。

另外,细心的你可能发现,上面代码中OSHelper对象声明处的上方 @Inject给注释掉了,这是什么意思呢?其实看过 Android 依赖注入: Dagger 2 实例讲解(一) 这篇博文的应该发现,这篇最后谈到的问题是和目前这个一样的,就是:

直接使用 @Inject 注入依赖对象的方式 和 通过 Component 接口提供依赖对象的方式到底有什么不一样? 下面通过源码层面来回答这个问题。

c、深入理解

直接使用 @Inject 注入依赖对象的方式 和 通过 Component 接口提供依赖对象的方式到底有什么不一样?

上面这句不知你是否明白?拿OSHelper这个类来说,直接使用@Inject注入依赖对象的方式 就是在MainActivity中通过如下代码实现:

1
2
  @Inject
  OSHelper osHelper;

这样注入之后,在 activityComponent.inject(this); 这句后面执行的代码都可以直接使用osHelper对象。

通过Component 接口提供依赖对象的方式 是指在ActivityComponent接口中添加 getOSHelper() 方法的方式,在使用的时候通过 activityComponent.getOSHelper() 得到OSHelper对象。

这样看来直接注入的方式更为简单一些,而且使用起来也较方便。没错,之前一直都是这样使用的。下面通过Dagger2生成的代码来简单分析这两种方式的不同。

先来看一下当前使用第二种方式下生成的ActivityComponent的源码:

@Generated("dagger.internal.codegen.ComponentProcessor")
public final class DaggerActivityComponent implements ActivityComponent {
    private DaggerActivityComponent(Builder builder) {
        assert builder != null;
        initialize(builder);
    }

    public static Builder builder() {
        return new Builder();
    }

    private void initialize(final Builder builder) {
    }

    @Override
    public void inject(MainActivity activity) {
        MembersInjectors.noOp().injectMembers(activity);
    }

    @Override
    public OSHelper getOSHelper() {
        return OSHelper_Factory.create().get();
    }

    public static final class Builder {
        private AppComponent appComponent;

        private Builder() {
        }

        public ActivityComponent build() {
            if (appComponent == null) {
                throw new IllegalStateException("appComponent must be set");
            }
            return new DaggerActivityComponent(this);
        }

        public Builder appComponent(AppComponent appComponent) {
            if (appComponent == null) {
                throw new NullPointerException("appComponent");
            }
            this.appComponent = appComponent;
            return this;
        }
    }
}

生成的代码很清晰,通过 Builder.build() 得到一个ActivityComponent对象,再通过 getOSHelper() 就可以得到一个OSHelper对象。这个过程很简单,为了与第一种方式做对比,请注意以下两个方法:

1
2
3
4
5
6
7
    private void initialize(final Builder builder) {
    }

    @Override
    public OSHelper getOSHelper() {
        return OSHelper_Factory.create().get();
    }

再看直接注入的方式生成的ActivityComponent代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
@Generated("dagger.internal.codegen.ComponentProcessor")
public final class DaggerActivityComponent implements ActivityComponent {
  private MembersInjector<MainActivity> mainActivityMembersInjector;

  private DaggerActivityComponent(Builder builder) {  
    assert builder != null;
    initialize(builder);
  }

  public static Builder builder() {  
    return new Builder();
  }

  private void initialize(final Builder builder) {  
    this.mainActivityMembersInjector = MainActivity_MembersInjector.create(
    (MembersInjector) MembersInjectors.noOp(), OSHelper_Factory.create());
  }

  @Override
  public void inject(MainActivity activity) {  
    mainActivityMembersInjector.injectMembers(activity);
  }

  public static final class Builder {
    private AppComponent appComponent;

    private Builder() {  
    }

    public ActivityComponent build() {  
      if (appComponent == null) {
        throw new IllegalStateException("appComponent must be set");
      }
      return new DaggerActivityComponent(this);
    }

    public Builder appComponent(AppComponent appComponent) {  
      if (appComponent == null) {
        throw new NullPointerException("appComponent");
      }
      this.appComponent = appComponent;
      return this;
    }
  }

对比一下,少了 getOSHelper() 方法,但 initialize 方法不在是一个空的方法:

private void initialize(final Builder builder) {  
    this.mainActivityMembersInjector = MainActivity_MembersInjector.create((MembersInjector) MembersInjectors.noOp(), OSHelper_Factory.create());
  }

可以看到在这个方法进行了 OSHelper_Factory.create() 这个操作,这个方法返回的是 Provider<OSHelper> osHelperProvider,用过Dagger1的同学也许对这个会比较熟悉,要得到OSHelper对象,只要调用Provider的 get()方法即可,osHelperProvider.get(). 那么这个方法什么时候会调用呢,我们再来继续看MainActivity_MembersInjector这个类:

@Generated("dagger.internal.codegen.ComponentProcessor")
public final class MainActivity_MembersInjector implements MembersInjector<MainActivity> {
  private final MembersInjector<AppCompatActivity> supertypeInjector;
  private final Provider<OSHelper> osHelperProvider;

  public MainActivity_MembersInjector(MembersInjector<AppCompatActivity> supertypeInjector, Provider<OSHelper> osHelperProvider) {  
    assert supertypeInjector != null;
    this.supertypeInjector = supertypeInjector;
    assert osHelperProvider != null;
    this.osHelperProvider = osHelperProvider;
  }

  @Override
  public void injectMembers(MainActivity instance) {  
    if (instance == null) {
      throw new NullPointerException("Cannot inject members into a null reference");
    }
    supertypeInjector.injectMembers(instance);
    instance.osHelper = osHelperProvider.get();
  }

  public static MembersInjector<MainActivity> create(MembersInjector<AppCompatActivity> supertypeInjector, Provider<OSHelper> osHelperProvider) {  
      return new MainActivity_MembersInjector(supertypeInjector, osHelperProvider);
  }
}

其中的injectMembers方法有调用上述get()方法,这个方法在DaggerActivityComponent的inject(MainActivity activity) 方法中被调用,也就说在注入时就会得到OSHelper对象。

两种方式的差异已经很清楚了,一个使用时创建对象,一个注入时就创建对象,前者符合Dagger2设计,避免了一开始就创建一堆对象。这就是我给出的结论,

d、源码

三、扩展链接

7435

分享本文:

Books