Dagger 2 subcomponentes vs dependências de componentes

135

O plus()método do Dagger 1 é algo que eu costumava usar em aplicativos anteriores, então eu entendo situações em que você pode querer ter um subcomponente com acesso total às ligações dos gráficos pai.

Em que situação seria benéfico usar uma dependência de componente em vez de uma dependência de subcomponente e por quê?

Bradley Campbell
fonte

Respostas:

228

Dependências de componentes - use isso quando desejar manter dois componentes independentes.

Subcomponentes - use-o quando desejar manter dois componentes acoplados.


Usarei o exemplo abaixo para explicar dependências e subcomponentes de componentes . Alguns pontos dignos de nota sobre o exemplo são:

  • SomeClassA1pode ser criado sem qualquer dependência. ModuleAfornece e instância de SomeClassA1via o provideSomeClassA1()método
  • SomeClassB1não pode ser criado sem SomeClassA1. ModuleBpode fornecer uma instância de SomeClassB1somente se uma instância de SomeClassA1for passada como um argumento para o provideSomeClassB1()método
@Module
public class ModuleA {
    @Provides
    public SomeClassA1 provideSomeClassA1() {
        return new SomeClassA1();
    }
}

@Module
public class ModuleB {
    @Provides
    public SomeClassB1 provideSomeClassB1(SomeClassA1 someClassA1) {
        return new SomeClassB1(someClassA1);
    }
}

public class SomeClassA1 {
    public SomeClassA1() {}
}

public class SomeClassB1 {
    private SomeClassA1 someClassA1;

    public SomeClassB1(SomeClassA1 someClassA1) {
        this.someClassA1 = someClassA1;
    }
}

O Dagger cuidará de passar a instância de SomeClassA1como um argumento para o provideSomeClassB1()método ModuleBsempre que a declaração de Componente / Subcomponente ModuleBfor inicializada. Precisamos instruir Dagger como cumprir a dependência. Isso pode ser feito usando dependência de componente ou subcomponente .

Dependência de componentes

Observe os seguintes pontos no exemplo de dependência de componente abaixo:

  • ComponentBdeve definir a dependência através do dependenciesmétodo na @Componentanotação.
  • ComponentAnão precisa declarar ModuleB. Isso mantém os dois componentes independentes.
public class ComponentDependency {
    @Component(modules = ModuleA.class)
    public interface ComponentA {
        SomeClassA1 someClassA1();
    }

    @Component(modules = ModuleB.class, dependencies = ComponentA.class)
    public interface ComponentB {
        SomeClassB1 someClassB1();
    }

    public static void main(String[] args) {
        ModuleA moduleA = new ModuleA();
        ComponentA componentA = DaggerComponentDependency_ComponentA.builder()
                .moduleA(moduleA)
                .build();

        ModuleB moduleB = new ModuleB();
        ComponentB componentB = DaggerComponentDependency_ComponentB.builder()
                .moduleB(moduleB)
                .componentA(componentA)
                .build();
    }
}

SubComponent

Observe os seguintes pontos no exemplo SubComponent:

  • Como ComponentBnão definiu a dependência ModuleA, ela não pode viver de forma independente. Torna-se dependente do componente que fornecerá o ModuleA. Por isso, tem uma @Subcomponentanotação.
  • ComponentAdeclarou ModuleBatravés do método de interface componentB(). Isso torna os dois componentes acoplados. De fato, ComponentBsó pode ser inicializado via ComponentA.
public class SubComponent {
    @Component(modules = ModuleA.class)
    public interface ComponentA {
        ComponentB componentB(ModuleB moduleB);
    }

    @Subcomponent(modules = ModuleB.class)
    public interface ComponentB {
        SomeClassB1 someClassB1();
    }

    public static void main(String[] args) {
        ModuleA moduleA = new ModuleA();
        ComponentA componentA = DaggerSubComponent_ComponentA.builder()
                .moduleA(moduleA)
                .build();

        ModuleB moduleB = new ModuleB();
        ComponentB componentB = componentA.componentB(moduleB);
    }
}
Praveer Gupta
fonte
4
Eu tenho uma configuração de subcomponente que não adiciona o Módulo B ao ComponenteA, o que significa que o construtor componentA não precisa do móduloB. Isso parece funcionar da maneira que eu esperava permitindo criação de COMPONENTA no arranque da aplicação e, em seguida, instanciar m
FriendlyMikhail
2
@ MikeN - Você pode destacar como se livrar do ModuleB no ComponentA? Eu posso me livrar do ModuleB no ComponentA somente se eu fornecer escopos diferentes no ComponentA e no ComponentB.
Praveer Gupta
1
Você está certo, minha configuração funciona porque eles estão em escopos diferentes. desculpas.
FriendlyMikhail
2
" SomeClassB1depende de SomeClassA1. ComponentAtem que definir explicitamente a dependência." ==> você quis dizer " ComponentBprecisa definir explicitamente a dependência"?
Tar
1
Da mesma forma que o @Tar apontou, entendo que em " SomeClassB1depende de SomeClassA1. ComponentANão precisa definir explicitamente a dependência". você quis dizer " ComponentBnão precisa definir explicitamente a dependência".
Sebas LG
45

De acordo com a documentação :

Component Dependencyfornece acesso apenas às ligações expostas como métodos de provisionamento por meio de dependências de componentes, ou seja, você tem acesso apenas aos tipos declarados no pai Component.

SubComponentdá a você acesso a todo o gráfico de ligação de seu pai quando ele é declarado, ou seja, você tem acesso a todos os objetos declarados em seus Modules.

Vamos dizer, você tem uma ApplicationComponentcontendo todas Androidcoisas relacionadas ( LocationService, Resources, SharedPreference, etc). Você também quer ter DataComponentonde gerenciar coisas para persistência e WebServicelidar com APIs. A única coisa que falta DataComponenté Application Contextque reside ApplicationComponent. A maneira mais simples de obter a Contextpartir de DataComponentseria uma dependência ApplicationComponent. Você precisa ter uma Contextdeclaração explícita ApplicationComponentporque só tem acesso a itens declarados. Nesse caso, não há trabalho manual, o que significa que você não precisa especificar Submodulesno pai Componente adicionar explicitamente seu submódulo a um módulo pai, como:

MySubcomponent mySubcomponent = myComponent.plus(new ChildGraphModule("child!")); // No need!

Agora, considere que caso onde você quer injetar WebServicea partir DataComponente LocationServicepartir ApplicationComponentem seus Fragmentque liga usando o @Submodule plusrecurso acima. O legal aqui é que o componente ao qual você está vinculando ( ApplicationComponent) não precisa expor WebServicenem LocationServiceporque você tem acesso a todo o gráfico imediatamente.

Eugene
fonte
2
Se bem entendi, não há interface chamada @Submodule. É um erro de digitação?
Islam Salah
Eu gosto de como isso usa um exemplo da vida real para mostrar a diferença. No entanto, isso é mais confuso do que ler os documentos. Seria bom ter menos classescomo exemplos e mais figuras para ilustrar o ponto exato.
sudocoder 14/04
18

Aqui está o exemplo de código com captura de tela para entender melhor o Component e o SubComponent:

Componente: insira a descrição da imagem aqui

  1. AppComponent contém duas declarações.
  2. AppComponent inicializa na classe App.
  3. O HomeActivityComponent depende do AppComponent.
  4. Em HomeActivity na inicialização do DaggerHomeActivityComponent, estou fornecendo o objeto AppComponent como uma composição.

Subcomponente:

insira a descrição da imagem aqui

  1. AppComponent contém SubComponent ou SubComponents.
  2. AppComponent inicializa na classe App.
  3. SubComponent não sabe sobre seu ParentComponent. Isso apenas fornecendo suas próprias dependências incluindo o Módulo.
  4. Em HomeActivity, estou injetando SubComponent usando seu componente pai.

E o diagrama pictórico: insira a descrição da imagem aqui

Fonte: link

0xAliHn
fonte
O diagrama não faria mais sentido se o subcomponente incluísse o AppComponent?
Florian Walther
1

Outra coisa que eu ainda não tinha percebido até agora é que:

  • Uma @Subcomponentinstância possui exatamente um componente pai (embora componentes diferentes possam instanciar o mesmo @Subcomponente ser o pai dessa instância)
  • A @Componentpode ter zero, um ou muitos componentes "principais" declarados através de dependências de componentes
arekolek
fonte
1
Provavelmente, no segundo caso, não é correto dizer que '@Component' pode ter ... pai (s). Em vez disso, '@Component' não tem pais, mas outros podem depender disso (basta usá-lo) por meio de dependências de componentes.
demaksee
@demaksee Eu não sei, parece-me que se você mapear sua hierarquia de componentes, você chegará ao DAG, e acho que é uma maneira padrão de se referir a essa relação como pai-filho no contexto do gráfico. Se estamos falando sobre o funcionamento interno de Dagger, acho que pode não ser a palavra certa.
Arekolek