Dagger2详解-从代码分析其原理

作者: 轻云时解被占用了 | 来源:发表于2016-08-05 14:43 被阅读1393次

    Dagger2基本概念请移步官方文档,或者我的翻译

    架构方面请关注GitHub(MVP+Retrofit+Dagger2+Okhttp)及我的文章Android UI框架快速搭建实践

    Dagger2是Dagger1的分支,由谷歌公司接手开发,目前的版本是2.0。Dagger2解决问题的基本思想是:利用生成和写的代码混合达到看似所有的产生和提供依赖的代码都是手写的样子。

    如果我们将Dagger2和1比较,他们两个在很多方面都非常相似,但也有很重要的区别,如下:

    1. 再也没有使用反射:图的验证、配置和预先设置都在编译的时候执行。
    1. 容易调试和可跟踪:完全具体地调用提供和创建的堆栈
    2. 更好的性能:谷歌声称他们提高了13%的处理性能
    3. 代码混淆:使用派遣方法,就如同自己写的代码一样

    当然所有这些很棒的特点都需要付出一个代价,那就是缺乏灵活性,例如:Dagger2没用反射所以没有动态机制。

    所以在Android UI框架快速搭建实践这篇文章中,因为我将Presenter作为泛型成员变量抽取到BaseFragment中了,所以View层的Presenter无法使用Dagger2实现注入。如果要实现Presenter注入,则需要在每个View实现中注入对应的Presenter实例,这样就无法抽取到基类中了。这个在具体实践中,需要你自己权衡。

    本文就GitHub项目,从生成的代码对Dagger2进行分析。

    使用Dagger2,在module的gradle中配置:

    apply plugin: 'com.android.application'
    apply plugin: 'com.neenbedankt.android-apt'
    
    android{
        ...
    }
    buildscript {
        repositories {
            jcenter()
        }
        dependencies {
            //添加apt插件
            classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
        }
    }
    dependencies {
        compile fileTree(dir: 'libs', include: ['*.jar'])
        compile 'com.google.dagger:dagger:2.4'
        apt 'com.google.dagger:dagger-compiler:2.4'
        ...
     }
    

    使用时,定义好component及module后,build工程,Dagger2编译器就会生成对应代码,然后在需要注入的地方调用生成的component实现,名为Dagger前缀+component名称的类完成绑定。

    先看结构图:

    dagger2.png
    大概介绍一下基本概念:
    • Module:使用@Module注解,封装@Provides方法,@Provides注解方法提供依赖对象。

    • @Inject:在需要依赖注入的地方使用。Dagger2提供3种方式:

      • 构造方法注入:在类的构造方法前面注释@Inject
      • 成员变量注入:在类的成员变量(非私有)前面注释@Inject
      • 函数方法注入:在函数前面注释@Inject
    • @Peractivity:自定义scope注解,约束依赖对象的生命周期。

    • @Singleton:api提供的scope注解,保证对象在对象图中唯一。

    • Component:使用@Component注解的接口,是@Inject和@Module联系的桥梁,子component可以使用@Subcomponent也可以指定@Component的dependency参数。

    是时候上代码了(Dagger2生成的代码在build/generated/source/apt/目录下):

    DemoApplicationModule的定义:

    @Module
    public class DemoApplicationModule {
      private final Application application;
    
      public DemoApplicationModule(Application application) {
        this.application = application;
      }
    
      /**
       * Expose the application to the graph.
       */
      @Provides @Singleton Application application() {
        return application;
      }
    
      @Provides @Singleton LocationManager provideLocationManager() {
        return (LocationManager) application.getSystemService(LOCATION_SERVICE);
      }
    }
    

    来看生成的代码:

    public final class DemoApplicationModule_ApplicationFactory implements Factory<Application> {
      private final DemoApplicationModule module;
    
      public DemoApplicationModule_ApplicationFactory(DemoApplicationModule module) {
        assert module != null;
        this.module = module;
      }
    
      @Override
      public Application get() {
        return Preconditions.checkNotNull(
            module.application(), "Cannot return null from a non-@Nullable @Provides method");
      }
    
      public static Factory<Application> create(DemoApplicationModule module) {
        return new DemoApplicationModule_ApplicationFactory(module);
      }
    }   
    ----------------
    public final class DemoApplicationModule_ProvideLocationManagerFactory
        implements Factory<LocationManager> {
      private final DemoApplicationModule module;
    
      public DemoApplicationModule_ProvideLocationManagerFactory(DemoApplicationModule module) {
        assert module != null;
        this.module = module;
      }
    
      @Override
      public LocationManager get() {
        return Preconditions.checkNotNull(
            module.provideLocationManager(),
            "Cannot return null from a non-@Nullable @Provides method");
      }
    
      public static Factory<LocationManager> create(DemoApplicationModule module) {
        return new DemoApplicationModule_ProvideLocationManagerFactory(module);
      }
    }
    

    module中定义的@Provides方法会生成对应的工厂类,实现Factory<T>接口,复写get()方法,get()中实际调用了module的provide方法,那module实例从哪里来的呢,看工厂类的创建方法中,看来这个module对象是需要外部传入的。

    ApplicationComponent定义:

    @Singleton  
    @Component(modules = DemoApplicationModule.class)
    public interface ApplicationComponent {
      // Field injections of any dependencies of the DemoApplication
      void inject(DemoApplication application);
    
      // Exported for child-components.
      Application application();
      LocationManager locationManager();
    }
    

    生成的代码:

    public final class DaggerApplicationComponent implements ApplicationComponent {
      private Provider<LocationManager> provideLocationManagerProvider;
    
      private MembersInjector<DemoApplication> demoApplicationMembersInjector;
    
      private Provider<Application> applicationProvider;
    
      private DaggerApplicationComponent(Builder builder) {
        assert builder != null;
        initialize(builder);
      }
    
      public static Builder builder() {
        return new Builder();
      }
    
      @SuppressWarnings("unchecked")
      private void initialize(final Builder builder) {
    
        this.provideLocationManagerProvider =
            DoubleCheck.provider(
                DemoApplicationModule_ProvideLocationManagerFactory.create(
                    builder.demoApplicationModule));
    
        this.demoApplicationMembersInjector =
            DemoApplication_MembersInjector.create(provideLocationManagerProvider);
    
        this.applicationProvider =
            DoubleCheck.provider(
                DemoApplicationModule_ApplicationFactory.create(builder.demoApplicationModule));
      }
    
      @Override
      public void inject(DemoApplication application) {
        demoApplicationMembersInjector.injectMembers(application);
      }
    
      @Override
      public Application application() {
        return applicationProvider.get();
      }
    
      @Override
      public LocationManager locationManager() {
        return provideLocationManagerProvider.get();
      }
    
      public static final class Builder {
        private DemoApplicationModule demoApplicationModule;
    
        private Builder() {}
    
        public ApplicationComponent build() {
          if (demoApplicationModule == null) {
            throw new IllegalStateException(
                DemoApplicationModule.class.getCanonicalName() + " must be set");
          }
          return new DaggerApplicationComponent(this);
        }
    
        public Builder demoApplicationModule(DemoApplicationModule demoApplicationModule) {
          this.demoApplicationModule = Preconditions.checkNotNull(demoApplicationModule);
          return this;
        }
      }
    }   
    

    DaggerApplicationComponent内部生成了Builder类,通过Builder的build()可以得到DaggerApplicationComponent对象,但必须先传入DemoApplicationModule对象,这是当然的,Component本来就只是一个桥梁而已,别忘了之前定义ApplicationComponent的注解@Component(modules = DemoApplicationModule.class),这样就好理解了吧。

    调用了Builder的build()后,会走DaggerApplicationComponent的构造器方法,这里调用了initialize(builder), initialize(builder)又干了什么呢?这里初始化了待注入的依赖对象locationManager和application,通过之前的工厂类的create()方法得到工厂类对象,工厂类是实现Factory<T>接口的,Factory<T>又是继承Provider<T>的,所以这里相当于拿到的是封装了不同对象的Provider实例, DoubleCheck.provider(provider)又干了什么呢?DoubleCheck也实现了Provider<T>,它的provider(provider)方法实际上返回了本身实例,实际上也是一个Provider<T>,但为什么要这么做呢?看代码:

    public static <T> Provider<T> provider(Provider<T> delegate) {
    ...    
        return new DoubleCheck<T>(delegate);
    }
      
    private DoubleCheck(Provider<T> provider) {
        assert provider != null;
        this.provider = provider;
    }
    
    public T get() {
        Object result = instance;
        if (result == UNINITIALIZED) {
          synchronized (this) {
            result = instance;
            if (result == UNINITIALIZED) {
              instance = result = provider.get();
            provider = null;
            }
          }
        }
        return (T) result;
      }
    

    provider(Provider<T> delegate)调用了构造器方法,构造器中将传入的Provider对象保存起来了,调用get()时会调用保存的provider对象的get(),实际上就是调用工厂方法的get()拿到对象,这样就实现了懒加载,在需要的时候调用get(),这时才会调用工厂方法的get(),因为真正创建对象的细节是封装在工厂类的get()中的,同时,它会将得到的对象缓存起来,这样下次调用就不需要再调用工厂类创建对象了。

    再看注入的地方:

    public class DemoApplication extends Application {
      private ApplicationComponent applicationComponent;
    
      @Inject LocationManager locationManager;  
      @Override public void onCreate() {
        super.onCreate();
        applicationComponent = DaggerApplicationComponent.builder()
            .demoApplicationModule(new DemoApplicationModule(this))
            .build();
      }
    
      public ApplicationComponent component() {
        return applicationComponent;
      }
    }
    

    这里需要注入locationMnager,我们再看根据@Inject生成的代码:

    public final class DemoApplication_MembersInjector implements MembersInjector<DemoApplication> {
      private final Provider<LocationManager> locationManagerProvider;
    
      public DemoApplication_MembersInjector(Provider<LocationManager> locationManagerProvider) {
        assert locationManagerProvider != null;
        this.locationManagerProvider = locationManagerProvider;
      }
    
      public static MembersInjector<DemoApplication> create(
          Provider<LocationManager> locationManagerProvider) {
        return new DemoApplication_MembersInjector(locationManagerProvider);
      }
    
      @Override
      public void injectMembers(DemoApplication instance) {
        if (instance == null) {
          throw new NullPointerException("Cannot inject members into a null reference");
        }
        instance.locationManager = locationManagerProvider.get();
      }
    
      public static void injectLocationManager(
          DemoApplication instance, Provider<LocationManager> locationManagerProvider) {
        instance.locationManager = locationManagerProvider.get();
      }
    }
    

    injectMembers(DemoApplication instance) 方法将locationManager对象赋值给DemoApplication对象,DemoApplication和locationManagerProvider从哪里来呢?上面DaggerApplicationComponent的initialize(builder)中实例化了DemoApplication_MembersInjector并传入需要的参数。

    DaggerApplicationComponent实现了ApplicationComponent,当然要复写其方法。
    在DemoApplication中调用DaggerApplicationComponent的inject()方法,然后就会调用DemoApplication_MembersInjector的injectMembers(DemoApplication instance),就实现了依赖注入。

    那DaggerApplicationComponent的其他方法呢?比如locationManager(),application()等呢,这些方法是在定义在ApplicationComponent中的,子component是共享父component中的绑定的,那子component怎么拿到父component中的依赖对象呢?

     this.locationManagerProvider =
            new Factory<LocationManager>() {
              private final ApplicationComponent applicationComponent = builder.applicationComponent;
    
              @Override
              public LocationManager get() {
                return Preconditions.checkNotNull(
                    applicationComponent.locationManager(),
                    "Cannot return null from a non-@Nullable component method");
              }
            };
    

    在DaggerHomeComponent的initialize(final Builder builder)中通过applicationComponent.locationManager,子component就也有locationManager实例了。

    Activity的依赖注入分析也是类似的,通过源码,我们可以清晰的跟踪其调用流程。

    That's all!希望能帮到你。

    相关文章

      网友评论

      • 芒果大神:已star ,如果能有dagger.android 包下的api 就更好了
      • glumes:所以,自己定义了 scope ,就会用 DoubleCheck 来作为 Provider 吗?

      本文标题:Dagger2详解-从代码分析其原理

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