Dagger2深入理解

作者: alighters | 来源:发表于2016-04-14 14:06 被阅读1805次

    最近,看到一些小伙伴想要入门Dagger2,加之最近刚经历了Dagger2的水深火热,在这里针对Dagger2中不同的注解方式,会生成怎样的代码,结合其生成的不同代码,来帮助大家做一些深入的理解。

    概念

    首先,Dagger2是一个DI的解决方案,跟之前接触过的Spring相比,它最主要的好处是通过apt插件在编译阶段时,用来生成注入的代码;而Spring是需要在运行时,通过XML或者注解来进行代码注入的。所以,相比来说,在性能上,Dagger2是优于Spring,但带来的是编译阶段时间的延长。这样的话,每当我们修改或者添加这些注解代码的时候,就需要我们重新Build一下(即由apt插件来生成我们所需要使用的代码),build时间的延长,感觉这对Android开发程序员来说,应该习以为常了吧。(掩面而泣。。。)

    另外,不得不提的就是apt插件的成熟。apt插件通过生成代码的方式会使得我们则针对特定规则的代码,通过添加注解,使用apt在编译阶段生成代码,减少我们的代码书写量。在gayhub上,已经有很多成熟的库,在使用apt来生成代码,像bundler这个库,就是通过封装Intent参数跟界面组件来绑定,这样我们就可不必通过getIntent来一个个获取参数。另外还提供了使用saveStaterestoreState,使得我们在界面组件异常退出的时候,不必再使用savedInstance来进行数据的保存与获取。还有就是支持Parcelable数据以及自定义数据parser,另作者不由不喜欢啊。

    使用

    根项目添加apt的版本依赖

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

    项目中配置apt插件的使用,以及Dagger2的版本

    apply plugin: 'com.neenbedankt.android-apt'
    
    dependencies {
       compile 'com.google.dagger:dagger:2.0.2'
       compile 'com.google.dagger:dagger-compiler:2.0.2'
       compile 'org.glassfish:javax.annotation:10.0-b28'
    }
    
    

    重要概念

    这里简单提一下它们的主要作用,dagger2两个很重要的东西,不过介绍的文章很多,需要的话,可自行查找一下。

    • Component

    用来为“被注入方“提供其所需要的注入类。当在子Scoped的Component中,也要被注入相同的类的时候,则需要在当前的Component添加相应的返回对应的类的方法。

    • Module

      用来提供上层所需要的依赖类。这里所带来的好处就是,所有的依赖类都是通过Module来提供出去,当我们的依赖发生改变时,我们只需要在这里改变提供一个新的对象类即可,而不影响我们上层使用的代码。这里,举一个最简单的例子就是,当我们调用服务器提供的API的时候,我们为上层提供了一个Repository命名的接口,并对其返回的是调用API的类;这里,当API还未实现,我们仅仅对其提供一个MockAPI即可,上层的调用者根本不需要关心我的数据提供从哪里来,是否真实。

    依赖实现辅助类

    • Provider

    Provider定义了一个来提供泛型T的方式,是个很简单的接口。

    public interface Provider<T> {
    
       /**
        * Provides a fully-constructed and injected instance of {@code T}.
        *
        * @throws RuntimeException if the injector encounters an error while
        *  providing an instance. For example, if an injectable member on
        *  {@code T} throws an exception, the injector may wrap the exception
        *  and throw it to the caller of {@code get()}. Callers should not try
        *  to handle such exceptions as the behavior may vary across injector
        *  implementations and even different configurations of the same injector.
        */
       T get();
    }
    
    
    • ScopedProvider

    ScopedProviderProvider的基础上,加上了Scoped的概念,主要通过指定类型的Factory来创建出来对应类型的ScopedProvider,在get方法上,主要通过double-checked来确保获取实例T的唯一性。

    /**
     * A {@link Provider} implementation that memoizes the result of a {@link Factory} instance.
     *
     * @author Gregory Kick
     * @since 2.0
     */
    public final class ScopedProvider<T> implements Provider<T> {
       private static final Object UNINITIALIZED = new Object();
    
       private final Factory<T> factory;
       private volatile Object instance = UNINITIALIZED;
    
       private ScopedProvider(Factory<T> factory) {
          assert factory != null;
          this.factory = factory;
       }
    
       @SuppressWarnings("unchecked") // cast only happens when result comes from the factory
          @Override
          public T get() {
             // double-check idiom from EJ2: Item 71
             Object result = instance;
             if (result == UNINITIALIZED) {
                synchronized (this) {
                   result = instance;
                   if (result == UNINITIALIZED) {
                      instance = result = factory.get();
                   }
                }
             }
             return (T) result;
          }
    
       /** Returns a new scoped provider for the given factory. */
       public static <T> Provider<T> create(Factory<T> factory) {
          if (factory == null) {
             throw new NullPointerException();
          }
          return new ScopedProvider<T>(factory);
       }
    }
    
    
    • Factory

    Factory仅仅继承了一个Provider,并是一个空的实现。

    public interface Factory<T> extends Provider<T> {}
    
    • MembersInjector

    从类的注释中,可以看出它的主要作用是给类的属性字段,或者方法参数来提供注入,并忽略是否在含有构造器的注入,都会生成MembersInjector的类。

    /**
     * Injects dependencies into the fields and methods on instances of type {@code T}. Ignores the
     * presence or absence of an injectable constructor.
     *
     * @param <T> type to inject members of
     *
     * @author Bob Lee
     * @author Jesse Wilson
     * @since 2.0 (since 1.0 without the provision that {@link #injectMembers} cannot accept
     *      {@code null})
     */
    public interface MembersInjector<T> {
    
       /**
        * Injects dependencies into the fields and methods of {@code instance}. Ignores the presence or
        * absence of an injectable constructor.
        *
        * <p>Whenever the object graph creates an instance, it performs this injection automatically
        * (after first performing constructor injection), so if you're able to let the object graph
        * create all your objects for you, you'll never need to use this method.
        *
        * @param instance into which members are to be injected
        * @throws NullPointerException if {@code instance} is {@code null}
        */
       void injectMembers(T instance);
    }
    
    

    注入方式

    这里通过列举几种不同的注入方式,探究其的实现,了解它们的不同使用场合

    • 构造器注入

    这里先看一个简单的类

    public class TestData {
    
       @Inject
       public TestData() {
       }
    }
    
    

    通过在构造器上添加@Inject注解,则会编译生成一个实现了Factory接口的单例类,用来生成TestData类。生成的代码如下:

    @Generated("dagger.internal.codegen.ComponentProcessor")
    public enum TestData_Factory implements Factory<TestData> {
       INSTANCE;
    
       @Override
       public TestData get() {  
          return new TestData()
       }
    
       public static Factory<TestData> create() {  
          return INSTANCE;
       }
    }
    

    可以看出通过构造器注入的生成的Factory类,是一个通过enum来实现的一个单例工厂类,是不是有个新技能Get。

    • Module注入

    实现一个Module来提供TestData的依赖,代码如下:

    @Module
    public class TestModule {
    
       @Singleton
       @Provides
       public TestData provideTestData(){
          return new TestData();
       }
    }
    

    再查看一下,通过apt生成之后的代码:

    @Generated("dagger.internal.codegen.ComponentProcessor")
    public final class TestModule_ProvideTestDataFactory implements Factory<TestData> {
       private final TestModule module;
    
       public TestModule_ProvideTestDataFactory(TestModule module) {  
          assert module != null;
          this.module = module;
       }
    
       @Override
          public TestData get() {  
             TestData provided = module.provideTestData();
             if (provided == null) {
                throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method");
             }
             return provided;
          }
    
       public static Factory<TestData> create(TestModule module) {  
          return new TestModule_ProvideTestDataFactory(module);
       }
    }
    
    

    可以看出,针对Module注解,dagger2会根据咱们定义了Provides的注解方法,会生成相应以Module为开头的Factory类,而这个Factory会以Module作为参数,通过调用Module中的代码,来实现注入类的提供。

    • 属性注入,方法注入

    这里,以我们常用的Activity为例,毕竟Activity的使用可是不允许我们通过构造器来生成一个Activity的,此时就是MembersInjector的用武之地了。
    来看个Activity的代码先。

    public class MainActivity extends AppCompatActivity {
    
       @Inject
       TestData mData;
    
       @Override
       protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.activity_main);
       }
    
    }
    
    

    当然,也可以使用方法来进行注入,像下面这样:

    @Inject
    public void setTestData(TestData testData){
    }
    

    会生成一个MainActivity_MembersInjector的类,如下:

    @Generated("dagger.internal.codegen.ComponentProcessor")
    public final class MainActivity_MembersInjector implements MembersInjector<MainActivity> {
       private final MembersInjector<AppCompatActivity> supertypeInjector;
       private final Provider<TestData> mDataProvider;
    
       public MainActivity_MembersInjector(MembersInjector<AppCompatActivity> supertypeInjector, Provider<TestData> mDataProvider) {  
          assert supertypeInjector != null;
          this.supertypeInjector = supertypeInjector;
          assert mDataProvider != null;
          this.mDataProvider = mDataProvider;
       }
    
       @Override
          public void injectMembers(MainActivity instance) {  
             if (instance == null) {
                throw new NullPointerException("Cannot inject members into a null reference");
             }
             supertypeInjector.injectMembers(instance);
             instance.mData = mDataProvider.get();
          }
    
       public static MembersInjector<MainActivity> create(MembersInjector<AppCompatActivity> supertypeInjector, Provider<TestData> mDataProvider) {  
          return new MainActivity_MembersInjector(supertypeInjector, mDataProvider);
       }
    }
    
    

    从上方可以看出,MainActivity_MembersInjector类通过实现MembersInjector类,通过injectMembers方法,来给MainActivity的实例,采用Provider获取实例的方式,来给我们之前定义的Inject参数或者属性,来赋值相应的注入类。
    另外,我们看到,在injectMembers的方法中,也会调用父类的supertypepInjector的类,来调用父类的参数注入。

    Component完成注入

    上面,我们提到了Activity所需要的类注入,只能通过方法参数,或者字段属性两个方式,(构造器是行不通的)。其MembersInjector的类页编写好了,我们该怎么调用呢?继续上面的例子,我们来给MainActivity来提供注入方式,神奇的Component就登上舞台了。

    @Singleton
    @Component(modules = { TestModule.class })
    public interface ApplicationComponent {
       void inject(MainActivity mainActivity);
    
    }
    

    这里,component一般在使用的时候,都是需要跟Scope相绑定的,不然会报错。看它的生成代码:

    @Generated("dagger.internal.codegen.ComponentProcessor")
    public final class DaggerApplicationComponent implements ApplicationComponent {
       private Provider<TestData> provideTestDataProvider;
       private MembersInjector<MainActivity> mainActivityMembersInjector;
    
       private DaggerApplicationComponent(Builder builder) {  
          assert builder != null;
          initialize(builder);
       }
    
       public static Builder builder() {  
          return new Builder();
       }
    
       public static ApplicationComponent create() {  
          return builder().build();
       }
    
       private void initialize(final Builder builder) {  
          this.provideTestDataProvider = ScopedProvider.create(TestModule_ProvideTestDataFactory.create(builder.testModule));
          this.mainActivityMembersInjector = MainActivity_MembersInjector.create((MembersInjector) MembersInjectors.noOp(), provideTestDataProvider);
       }
    
       @Override
          public void inject(MainActivity mainActivity) {  
             mainActivityMembersInjector.injectMembers(mainActivity);
          }
    
       public static final class Builder {
          private TestModule testModule;
    
          private Builder() {  
          }
    
          public ApplicationComponent build() {  
             if (testModule == null) {
                this.testModule = new TestModule();
             }
             return new DaggerApplicationComponent(this);
          }
    
          public Builder testModule(TestModule testModule) {  
             if (testModule == null) {
                throw new NullPointerException("testModule");
             }
             this.testModule = testModule;
             return this;
          }
       }
    }
    
    

    我们定义好的Component接口,都会生成一个以Dagger开头的一个实现类。在其中,就可以看到它在实现咱们定义的void inject(MainActivity activity)方法,就是通过使用MainActivity_MembersInjector类,来完成这一步注入的,有木有颇感神奇啊。这就是apt插件的神奇之处,定义好了规则,按照规则来生成代码即可。

    这里,紧接着提及一下Scope的的用法。当我们为Component定义了Scope之后,并且在module的方法上,添加了Scope注解,这样Dagger2在Component中生成相应的Provider的时候,就会在前面添加ScopedProvider,来对我们所需要的Provider来提供单例模式的访问。具体可以看到的代码就是上面初始化过程中,生成provideTestDataProvider的字段。

    总结

    掌握了上面提及的这些内容,则Dagger2的不同注入方法,生成怎样的代码,我们就上手了Dagger2的使用,若是在使用中还遇到问题的话,可加入下方的二维码群,来进行交流。另外,感谢明道小伙伴们(月半兄jagerlchad)对Dagger2的讨论。

    Paste_Image.png

    相关文章

      网友评论

      本文标题:Dagger2深入理解

      本文链接:https://www.haomeiwen.com/subject/morgkttx.html