Dagger 2学习与探索(八)

作者: akak18183 | 来源:发表于2017-08-28 11:27 被阅读0次

    上一期介绍了Component dependency方法,这一期介绍SubComponent方法,效果是类似的,只是实现手段有一点不一样。
    SubComponent的声明不再是用@Component标识,而是使用@Subcomponent标识,不再有dependency参数,同时上级Component也无需再添加暴露函数。
    那么SubComponent是如何与上级建立联系的呢?
    有两种方式:

    • 在上级Component内添加获得该SubComponent的抽象方法;
    • 从v2.7开始,允许在SubComponent内添加builder,使用build()方法来获取实例,同时还需要在上级Component内添加SubComponentbinder的相关代码,来实现映射。这种方法坏处是复杂很多,好处是解耦?以我目前看来,虽然不用写工厂方法了,可还是要加更多新的东西到上级,代价实在不对等。不过既然这个功能在这里,还是了解一下比较好。

    第一种方法

    主体代码

    和上一期的非常类似,稍微做一下修改:
    AppComponent不用再暴露ClassA的方法,而加上ActivityComponent的构造方法:

    @PerApplication
    @Component(modules = AppModule.class)
    public interface AppComponent {
    
      void inject(MyApp myApp);
      
      ActivityComponent getActivityComponent(ActivityModule activityModule);
    }
    

    注意和提供/暴露函数不太一样,这里虽然ActivityComponent自己有范围标注,但在这里不必标出。你也可以标出,但不会造成任何区别。ActivityComponent不是注入对象,并不会变成单例模式。

    @PerActivity
    @Subcomponent(modules = ActivityModule.class)
    public interface ActivityComponent {
    
      void inject(MainActivity mainActivity);
    }
    

    Module部分没有变化。然后就是使用的时候有区别:

    public class MainActivity extends AppCompatActivity {
      @Inject ClassB classB;
    
      @Override
      protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    
        MyApp.getAppComponent().getActivityComponent(new ActivityModule(4)).inject(this);
      }
    }
    

    现在ActivityComponent是由AppComponent产生。运行之前还是要rebuild一下。
    可以看到,这种方法和上一期的难易程度相差不大,不过由于Dagger1的时代就有类似Component dependency的模式,因此可能Component dependency用的更多一些。

    生成代码

    还是来看看DaggerAppComponent吧。现在DaggerActivityComponent已经不存在了。

    public final class DaggerAppComponent implements AppComponent {
      private Provider<Integer> provideIntAProvider;
    
      private Provider<Integer> provideIntBProvider;
    
      private Provider<ClassA> provideClassAProvider;
    
      private MembersInjector<MyApp> myAppMembersInjector;
    
      private DaggerAppComponent(Builder builder) {
        assert builder != null;
        initialize(builder);
      }
    
      public static Builder builder() {
        return new Builder();
      }
    
      @SuppressWarnings("unchecked")
      private void initialize(final Builder builder) {
    
        this.provideIntAProvider = AppModule_ProvideIntAFactory.create(builder.appModule);
    
        this.provideIntBProvider = AppModule_ProvideIntBFactory.create(builder.appModule);
    
        this.provideClassAProvider =
            DoubleCheck.provider(
                AppModule_ProvideClassAFactory.create(
                    builder.appModule, provideIntAProvider, provideIntBProvider));
    
        this.myAppMembersInjector = MyApp_MembersInjector.create(provideClassAProvider);
      }
    
      @Override
      public void inject(MyApp myApp) {
        myAppMembersInjector.injectMembers(myApp);
      }
    
      @Override
      public ActivityComponent getActivityComponent(ActivityModule activityModule) {
        return new ActivityComponentImpl(activityModule);
      }
    
      public static final class Builder {
        private AppModule appModule;
    
        private Builder() {}
    
        public AppComponent build() {
          if (appModule == null) {
            throw new IllegalStateException(AppModule.class.getCanonicalName() + " must be set");
          }
          return new DaggerAppComponent(this);
        }
    
        public Builder appModule(AppModule appModule) {
          this.appModule = Preconditions.checkNotNull(appModule);
          return this;
        }
      }
    
      private final class ActivityComponentImpl implements ActivityComponent {
        private final ActivityModule activityModule;
    
        private Provider<Integer> provideCProvider;
    
        private Provider<ClassB> provideClassBProvider;
    
        private MembersInjector<MainActivity> mainActivityMembersInjector;
    
        private ActivityComponentImpl(ActivityModule activityModule) {
          this.activityModule = Preconditions.checkNotNull(activityModule);
          initialize();
        }
    
        @SuppressWarnings("unchecked")
        private void initialize() {
    
          this.provideCProvider = ActivityModule_ProvideCFactory.create(activityModule);
    
          this.provideClassBProvider =
              DoubleCheck.provider(
                  ActivityModule_ProvideClassBFactory.create(
                      activityModule, DaggerAppComponent.this.provideClassAProvider, provideCProvider));
    
          this.mainActivityMembersInjector = MainActivity_MembersInjector.create(provideClassBProvider);
        }
    
        @Override
        public void inject(MainActivity mainActivity) {
          mainActivityMembersInjector.injectMembers(mainActivity);
        }
      }
    }
    

    可见其内部新生成了一个类,ActivityComponentImpl,效果就像把上一期的DaggerActivityComponent合并到这里来。

    第二种方法

    主体代码
    1. ActivityComponent
    @PerActivity
    @Subcomponent(modules = ActivityModule.class)
    public interface ActivityComponent {
    
      void inject(MainActivity mainActivity);
    
      @Subcomponent.Builder
      interface Builder extends SubcomponentBuilder<ActivityComponent> {
        Builder withActivityModule(ActivityModule activityModule);
      }
    }
    

    现在AppComponent没有工厂方法了,ActivityComponent需要提供一个Builder来用参数构造自己。注意到@Subcomponent.Builder标注,看看究竟:

    /**
     * A subcomponent that inherits the bindings from a parent {@link Component} or
     * {@link Subcomponent}. The details of how to associate a subcomponent with a parent are described
     * in the documentation for {@link Component}.
     *
     * @author Gregory Kick
     * @since 2.0
     */
    @Retention(RUNTIME) // Allows runtimes to have specialized behavior interoperating with Dagger.
    @Target(TYPE)
    @Documented
    public @interface Subcomponent {
      /**
       * A list of classes annotated with {@link Module} whose bindings are used to generate the
       * subcomponent implementation.  Note that through the use of {@link Module#includes} the full set
       * of modules used to implement the subcomponent may include more modules that just those listed
       * here.
       */
      Class<?>[] modules() default {};
      
      /**
       * A builder for a subcomponent.  This follows all the rules of {@link Component.Builder}, except
       * it must appear in classes annotated with {@link Subcomponent} instead of {@code Component}.
       * Components can have methods that return a {@link Subcomponent.Builder}-annotated type,
       * allowing the user to set modules on the subcomponent using their defined API.
       */
      @Target(TYPE)
      @Documented
      @interface Builder {}
    }
    

    正如注释文档描述的那样,Builder接口需要有一个方法来帮助设置ActivityModule,在这里就是withActivityModule方法,名字仍旧不重要。
    SubcomponentBuilder是自己定义的一个接口,好在如果使用第二种方式,这个接口可以复用在其他Subcomponent里:

    public interface SubcomponentBuilder<V> {
      V build();
    }
    

    名字不重要,重要的是要带有build()方法。这个接口与Builder配合来实现Builder.build()

    1. AppComponent
    @PerApplication
    @Component(modules = {AppModule.class, ActivityBinders.class})
    public interface AppComponent {
    
      void inject(MyApp myApp);
    
      Map<Class<?>, Provider<SubcomponentBuilder>> subcomponentBuilders();
    }
    

    首先介绍一下ActivityBinders,这个负责获取对应的Builder

    @Module(subcomponents={ ActivityComponent.class })
    public abstract class ActivityBinders {
    
      @Binds
      @IntoMap
      @SubcomponentKey(Builder.class)
      public abstract SubcomponentBuilder getActivityBuilder(Builder impl);
    }
    

    注意其标注,属于@Module而且要声明对应的Subcomponent
    出现了三个新标注。
    @Binds说是新标注,其实在其他地方也见过。
    @IntoMap

    /**
     * The method's return type forms the type argument for the value of a {@code Map<K, Provider<V>>},
     * and the combination of the annotated key and the returned value is contributed to the map as a
     * key/value pair. The {@code Map<K, Provider<V>>} produced from the accumulation of values will be
     * immutable.
     *
     * @see <a href="https://google.github.io/dagger/multibindings#map-multibindings">Map multibinding</a>
     */
    @Documented
    @Target(METHOD)
    @Retention(RUNTIME)
    public @interface IntoMap {}
    

    大意是标注的方法会和某个Map<K, Provider<V>>建立联系,准确地说是返回类型和标注类型会成为一个键值对。
    返回类型这里自然是SubcomponentBuilder的实现类,标注类型就是下面@SubcomponentKey(Builder.class)标注的Builder.class,这个Builder就是ActivityComponent里面的Builder
    SubcomponentKey也是要自己添加的一个标注接口,好在还是可以复用。

    @MapKey
    @Target({ElementType.METHOD}) 
    @Retention(RetentionPolicy.RUNTIME)
    public @interface SubcomponentKey {
      Class<?> value();
    }
    

    其实总的来说,现在AppComponent不直接产生ActivityComponent,而是生成一个Map,key就是Builder的类(由@SubcomponentKey规定,当然你也可以直接用@MapKey,不过具体实现有点不同),value自然就是对应的BuilderProvider封装的工厂。ActivityComponent自己查表获取Builder再传入ActivityModule完成建造,从而再注入。
    当然了,这里使用了一些帮助的扩展接口/标注,从而使得添加多个Subcomoponent变得容易。不用也是完全可以的。

    1. MainActivity
      基于2的分析,这里的代码思路就很清晰了:
    public class MainActivity extends AppCompatActivity {
      @Inject ClassB classB;
    
      @Override
      protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    
        ActivityComponent.Builder builder
            = (ActivityComponent.Builder) MyApp.getAppComponent()
            .subcomponentBuilders()
            .get(ActivityComponent.Builder.class)
            .get();
        builder.withActivityModule(new ActivityModule(4)).build().inject(this);
      }
    }
    
    生成代码

    DaggerAppComponent

    public final class DaggerAppComponent implements AppComponent {
      private Provider<Integer> provideIntAProvider;
    
      private Provider<Integer> provideIntBProvider;
    
      private Provider<ClassA> provideClassAProvider;
    
      private MembersInjector<MyApp> myAppMembersInjector;
    
      private Provider<ActivityComponent.Builder> activityComponentBuilderProvider;
    
      private Provider<SubcomponentBuilder> getActivityBuilderProvider;
    
      private Provider<Map<Class<?>, Provider<SubcomponentBuilder>>>
          mapOfClassOfAndProviderOfSubcomponentBuilderProvider;
    
      private DaggerAppComponent(Builder builder) {
        assert builder != null;
        initialize(builder);
      }
    
      public static Builder builder() {
        return new Builder();
      }
    
      @SuppressWarnings("unchecked")
      private void initialize(final Builder builder) {
    
        this.provideIntAProvider = AppModule_ProvideIntAFactory.create(builder.appModule);
    
        this.provideIntBProvider = AppModule_ProvideIntBFactory.create(builder.appModule);
    
        this.provideClassAProvider =
            DoubleCheck.provider(
                AppModule_ProvideClassAFactory.create(
                    builder.appModule, provideIntAProvider, provideIntBProvider));
    
        this.myAppMembersInjector = MyApp_MembersInjector.create(provideClassAProvider);
    
        this.activityComponentBuilderProvider =
            new dagger.internal.Factory<ActivityComponent.Builder>() {
              @Override
              public ActivityComponent.Builder get() {
                return new ActivityComponentBuilder();
              }
            };
    
        this.getActivityBuilderProvider = (Provider) activityComponentBuilderProvider;
    
        this.mapOfClassOfAndProviderOfSubcomponentBuilderProvider =
            MapProviderFactory.<Class<?>, SubcomponentBuilder>builder(1)
                .put(ActivityComponent.Builder.class, getActivityBuilderProvider)
                .build();
      }
    
      @Override
      public void inject(MyApp myApp) {
        myAppMembersInjector.injectMembers(myApp);
      }
    
      @Override
      public Map<Class<?>, Provider<SubcomponentBuilder>> subcomponentBuilders() {
        return mapOfClassOfAndProviderOfSubcomponentBuilderProvider.get();
      }
    
      public static final class Builder {
        private AppModule appModule;
    
        private Builder() {}
    
        public AppComponent build() {
          if (appModule == null) {
            throw new IllegalStateException(AppModule.class.getCanonicalName() + " must be set");
          }
          return new DaggerAppComponent(this);
        }
    
        public Builder appModule(AppModule appModule) {
          this.appModule = Preconditions.checkNotNull(appModule);
          return this;
        }
      }
    
      private final class ActivityComponentBuilder implements ActivityComponent.Builder {
        private ActivityModule activityModule;
    
        @Override
        public ActivityComponent build() {
          if (activityModule == null) {
            throw new IllegalStateException(ActivityModule.class.getCanonicalName() + " must be set");
          }
          return new ActivityComponentImpl(this);
        }
    
        @Override
        public ActivityComponentBuilder withActivityModule(ActivityModule activityModule) {
          this.activityModule = Preconditions.checkNotNull(activityModule);
          return this;
        }
      }
    
      private final class ActivityComponentImpl implements ActivityComponent {
        private Provider<Integer> provideCProvider;
    
        private Provider<ClassB> provideClassBProvider;
    
        private MembersInjector<MainActivity> mainActivityMembersInjector;
    
        private ActivityComponentImpl(ActivityComponentBuilder builder) {
          assert builder != null;
          initialize(builder);
        }
    
        @SuppressWarnings("unchecked")
        private void initialize(final ActivityComponentBuilder builder) {
    
          this.provideCProvider = ActivityModule_ProvideCFactory.create(builder.activityModule);
    
          this.provideClassBProvider =
              DoubleCheck.provider(
                  ActivityModule_ProvideClassBFactory.create(
                      builder.activityModule,
                      DaggerAppComponent.this.provideClassAProvider,
                      provideCProvider));
    
          this.mainActivityMembersInjector = MainActivity_MembersInjector.create(provideClassBProvider);
        }
    
        @Override
        public void inject(MainActivity mainActivity) {
          mainActivityMembersInjector.injectMembers(mainActivity);
        }
      }
    }
    

    首先有些新的东西是下游的Builder的实现细节,很典型的建造者模式,这里就不过多分析了,主要是探究Map是怎么生成的。
    注意到

      @Override
      public Map<Class<?>, Provider<SubcomponentBuilder>> subcomponentBuilders() {
        return mapOfClassOfAndProviderOfSubcomponentBuilderProvider.get();
      }
    

    这个Map同样也被Provider接口封装,具体在

    this.mapOfClassOfAndProviderOfSubcomponentBuilderProvider =
            MapProviderFactory.<Class<?>, SubcomponentBuilder>builder(1)
                .put(ActivityComponent.Builder.class, getActivityBuilderProvider)
                .build();
    

    这里进行初始化。MapProviderFactory实际上就是一个Map的工厂,把键值对放进去,然后使用get()来获取Map:

    /**
     * A {@link Factory} implementation used to implement {@link Map} bindings. This factory returns a
     * {@code Map<K, Provider<V>>} when calling {@link #get} (as specified by {@link Factory}).
     细节省略……
    

    那么.put(ActivityComponent.Builder.class, getActivityBuilderProvider)就是放键值对,getActivityBuilderProvider又从哪里来?this.getActivityBuilderProvider = (Provider) activityComponentBuilderProvider;
    然后

        this.activityComponentBuilderProvider =
            new dagger.internal.Factory<ActivityComponent.Builder>() {
              @Override
              public ActivityComponent.Builder get() {
                return new ActivityComponentBuilder();
              }
            };
    

    如此就把一个初始化好的ActivityComponentBuilder插入到了Map中。
    之后发生的就很自然了。
    总而言之,这种方法复杂度比第一种高得很明显。少写了一行ActivityComponent的工厂函数,代价是多了一个ActivityBinders以及配套的接口/注解,同时AppComponent还得添加其他东西。至于好处那就见仁见智了。

    相关文章

      网友评论

        本文标题:Dagger 2学习与探索(八)

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