首页 > 代码库 > Dagger 2从浅到深(七)

Dagger 2从浅到深(七)

在使用Dagger 2开发时,一般都是在Application中生成一个AppComponent,然后其他的功能模块的Component依赖于AppComponent,作为AppComponent的子组件。可是,对于将自组建添加到父组件有两种方式:

  1. 通过@Component的dependencies属性依赖父组件

    @Component(modules = OrangeModule.class, dependencies = FruitComponent.class)
    public interface OrangeComponent {
        ***
    }
    
  2. 通过Moudle的subcomponents属性添加子组件

    @Module(subcomponents = AppleSubcomponent.class)
    public class FruitModule {
    
        ***
    }
    

自己也是照葫芦画瓢采用了第一种方式,对于子组件(@Subcomponent)的认识也是一知半解,总是不能合理的封装,本篇文章深入了解@Subcomponent。

Subcomponent的介绍

对于继承,大家应该都不陌生,其实就是子类继承父类,子类自动获取了的父类的属性和行为。我觉得我们也可以这么认为子组件。子组件是继承和扩展父组件的对象的组件。我们可以将应用程序的各个功能分割为模块,以将应用程序的不同部分彼此封装或在组件中使用多个范围。这不就是Android的模块化开发。

  • 绑定在子组件中的对象可以取决于绑定在其父组件或任何继承自组件中的任何对象,以及绑定在其自己的模块中的对象。
  • 绑定在父组件中的对象不能依赖于在子组件中绑定的对象; 绑定在一个子组件中的对象也不能取决于兄弟子组件中绑定的对象。

换句话说,相对于对象而言,自组件这么说 - 你的是我的,我的还是我的。父组件只能这么说 - 你的不是我的,我的是我的。

添加子组件

声明子组件

声明子组件,与普通组件声明类似,可以通过编写一个抽象类或接口来创建一个子组件,该类或接口声明了返回应用程序相关的类型的抽象方法。可以使用@Component,也可以使用@Subcomponent注解,这个没有一定的强制性。

与@Component注解不同的是,使用@Subcomponent注解子组件,必须声明其xxComponent.Builder,否则编译时,会报错。

@Subcomponent(modules = AppleModule.class)
public interface AppleSubcomponent {

    AppleBean supplyApple();

    @Subcomponent.Builder
    interface Builder{
        Builder appleModule(AppleModule module);
        AppleSubcomponent build();
    }
}

通过@Component的dependencies属性依赖父组件

  1. 声明Module:OrangeModuleModule和FruitModule,其中OrangeModuleModule提供一个OrangeBean对象,而FruitModule提供一个Fruit对象

    @Module
    public class OrangeModule {
    
        @Provides
        public OrangeBean provideOrange() {
            return new OrangeBean("这是一个橘子");
        }
    }
    
    
    @Module
    public class FruitModule {
    
        @Provides
        public Fruits provideFruit() {
            return new Fruits("这是一个水果");
        }
    }
    
  2. 声明Component,并添加依赖关系

    @Component(modules = OrangeModule.class, dependencies = FruitComponent.class)
    public interface OrangeComponent {
        void inject(OrangeFragment fragment);
    }
    
    @Component(modules = {FruitModule.class})
    public interface FruitComponent {
    
        ***
    
        // 将FruitModule中的Fruits暴露出来,以便于其他依赖于FruitComponent的Component调用
        // 若不将Fruits暴露出来,依赖于FruitComponent的Component无法获取该实例,此时编译会报错,提示为该实例提供@Inject注解或者@Provides方法
        Fruits supplyFruits();
    }
    
  3. 注入依赖对象

    public class OrangeFragment extends Fragment {

    ***
    
    @Inject
    OrangeBean mOrangeBean;
    @Inject
    Fruits mFruits;
    
    public OrangeFragment() {
    }
    
    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        FruitComponent fruitComponent = DaggerFruitComponent.create();
        OrangeComponent orangeComponent = DaggerOrangeComponent.builder()
                .fruitComponent(fruitComponent)
                .build();
        orangeComponent.inject(this);
        super.onCreate(savedInstanceState);
    }
    
    ***
    

    }

  4. 可以清晰的看出依赖关系,在示例中,OrangeComponent依赖于FruitComponent

  5. 对于每个Component都会生成DaggerXxComponent实例


注意:

依赖Component(OrangeComponent) 仅继承 被依赖Component(FruitComponent) 中显示提供的依赖。如果被依赖Component(FruitComponent)必须提供依赖注入的对象,也就是将对象实例暴露出来。否则,在子组件,也就是依赖Component(OrangeComponent)中,无法使用@Inject注入被依赖的Component(AppComponent)中的对象,此时编译器会报错,提示依赖注入的Xx实例没有提供@Inject注解或者@Provides方法。

通过Moudle的subcomponents属性添加子组件

  1. 声明AppleModule

    @Module
    public class AppleModule {
    
        @Provides
        public AppleBean privdeApple() {
            return new AppleBean("这是一个苹果");
        }
    }
    
  2. 声明@SubComponent

    @Subcomponent(modules = AppleModule.class)
    public interface AppleSubcomponent {
    //
    AppleBean supplyApple();

    @Subcomponent.Builder
    interface Builder{
        Builder appleModule(AppleModule module);
        AppleSubcomponent build();
    }
    

    }

  3. 声明FruitModule,将AppleSubcomponent添加到subcomponents中

    @Module(subcomponents = {AppleSubcomponent.class})
    public class FruitModule {
    
        @Provides
        public Fruits provideFruit() {
            return  new Fruits("这是一个水果");
        }
    
        @Provides
        @Type(value = "http://www.mamicode.com/apple")
        public Fruits provideSubApple(AppleSubcomponent.Builder builder) {
            return new Fruits(builder
                    .appleModule(new AppleModule())
                    .build()
                    .supplyApple()
                    .toString());
        }
    }
    
  4. 声明FruitComponent,并将AppleSubcomponent.Builder暴露出来,以便依赖注入子组件的Builder

    @Component(modules = {FruitModule.class})
    public interface FruitComponent {
    
        void inject(FruitActivity activity);
    
        ****
    
        AppleSubcomponent.Builder supplyAppleSubcomponentBuilder();
    }
    
  5. 依赖注入对象

    public class FruitActivity extends AppCompatActivity {
    
        ***
    
        // 依赖注入子组件的Builder
        @Inject
        Provider<AppleSubcomponent.Builder> appleBulder;
        AppleSubcomponent mAppleSubcomponent;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            FruitComponent fruitComponent = DaggerFruitComponent.builder()
                    .build();
            fruitComponent.inject(this);
            super.onCreate(savedInstanceState);
            setContentView(getLayoutId());
    
            ***
    
            mAppleSubcomponent = appleBulder.get().build();
            AppleBean mAppleBean = mAppleSubcomponent.supplyApple();
        }
    
        ***
    }
    

关于SubComponent:

  1. 不需要在被依赖的Component中显示提供依赖
  2. @Subcomponent不会生成DaggerXxComponent实例,也就意味着不需要使用更多的DaggerXXXXComponent对象来创建依赖。
  3. 将子组件添加到父组件中,只需将所注入的@Module的subcomponents属性中,然后父组件可以访问子组件的Builder

@Scope

Dagger 2从浅到深(四)已经提到,在Dagger中,通过使用@Scope对组件进行注解,可以将组件与组件的生命周期相关联,实际上对注入器的控制。在这种情况下,组件实现保存对所有作用域对象的引用,从而可以重用它们。@Scope注解的@Provides方法的Module只能注入到到具有相同作用域的注解的组件中。

对于@Inject注解构造函数的类,也可以使用@Scope进行注解,这些“隐式绑定”可以被该作用域或任何其后代组件注释的任何组件使用,作用域实例将被绑定在被注解的作用域范围内。

正是这样,任何子组件与父组件不能使用相同的@Scope注解,因为子组件是在父组件中创建的,所以其生命周期应在父组件生命周期范围内,意味着子组件的生命周期与父组件的生命周期存在包含关系。比如:

@RootScope @Component
interface RootComponent {
  BadChildComponent.Builder badChildComponent(); // ERROR!
  SiblingComponentOne.Builder siblingComponentOne();
  SiblingComponentTwo.Builder siblingComponentTwo();
}

@RootScope @Subcomponent
interface BadChildComponent {...}

@ChildScope @Subcomponent
interface SiblingComponentOne {...}

@ChildScope @Subcomponent
interface SiblingComponentTwo {...}

在示例中,BadChildComponent具有与父对象RootComponent相同的@RootScope注解,这是一个错误注解。但是SiblingComponentOne和SiblingComponentTwo都使用@ChildScope,因为两个子组件绑定同一个对象并不会产生混淆。

对于子组件而言,两个子组件使用相同的@Scope注解,这两个子组件内容的@Scope注解的实例是相对独立,互不关联。

@Singleton @Component
interface RootComponent {
  SessionComponent.Builder sessionComponent();
}

@SessionScope @Subcomponent
interface SessionComponent {
  FooRequestComponent.Builder fooRequestComponent();
  BarRequestComponent.Builder barRequestComponent();
}

@RequestScope @Subcomponent
interface FooRequestComponent {...}

@RequestScope @Subcomponent
interface BarRequestComponent {...}

在示例中,RootComponent使用@Singleton注解。@SessionScope嵌套在@Singleton范围内,@RequestScope嵌套在@SessionScope中。值得注意的是,FooRequestComponent和BarRequestComponent尽管都用@RequestScope注解,但是它们都是SessionComponent的子组件,是相互独立的。

  • RootComponent使用@Singleton注解
  • @SessionScope嵌套在@Singleton范围内
  • @RequestScope嵌套在@SessionScope中
  • FooRequestComponent和BarRequestComponent都用@RequestScop注解

复用Module

当相同的Module类型注入在父组件及其任何子组件中时,则每个组件将自动使用该模块的相同实例。 这意味着如果您为重复的模块调用子组件构建器方法,或者子组件工厂方法将重复的模块定义为参数,则会出现错误。(前者在编译时无法检查,因此是运行时错误。)

@Component(modules = {RepeatedModule.class, ...})
interface ComponentOne {
  ComponentTwo componentTwo(RepeatedModule repeatedModule); // COMPILE ERROR!
  ComponentThree.Builder componentThreeBuilder();
}

@Subcomponent(modules = {RepeatedModule.class, ...})
interface ComponentTwo { ... }

@Subcomponent(modules = {RepeatedModule.class, ...})
interface ComponentThree {
  @Subcomponent.Builder
  interface Builder {
    Builder repeatedModule(RepeatedModule repeatedModule);
    ComponentThree build();
  }
}

DaggerComponentOne.create().componentThreeBuilder()
    .repeatedModule(new RepeatedModule()) // 运行时报错
    .build();

总结

好像有点懂了,从下图更能清楚地看清楚,两种添加子组件的区别:

  1. 通过@Component的dependencies属性依赖父组件,这种方式体现更多的是父组件和子组件之间更多的是依赖关系。比如,Appcomponent中提供Context,以便其他子Component依赖注入。
  2. 通过Moudle的subcomponents属性添加子组件,这种方式体现更多的是父组件和子组件之间更多的是包含关系,这种方式更多的用于功能的封装,比如数据库操作,有的界面需要,有的不需要,可以将数据库操作封装成子组件。

技术分享

<script type="text/javascript"> $(function () { $(‘pre.prettyprint code‘).each(function () { var lines = $(this).text().split(‘\n‘).length; var $numbering = $(‘
    ‘).addClass(‘pre-numbering‘).hide(); $(this).addClass(‘has-numbering‘).parent().append($numbering); for (i = 1; i <= lines; i++) { $numbering.append($(‘
  • ‘).text(i)); }; $numbering.fadeIn(1700); }); }); </script>

    Dagger 2从浅到深(七)