Dagger2: Cómo usar @Provides y @Binds en el mismo módulo

4 minutos de lectura

Estoy usando el nuevo Dagger2 (ver 2.11) y estoy usando las nuevas características como AndroidInjectory ContributesAndroidInjector. Tengo un subcomponente de actividad,

        @Module
        abstract class ActivityBuilderModule {
            @ContributesAndroidInjector(modules = 
                   {UserListModule.class, MainFragmentModule.class})
            @ActivityScope
            abstract MainActivity bindsMainActivity();

        }



  @Module
  public abstract class MainFragmentModule {
    @ContributesAndroidInjector
    @FragmentScope
    @FragmentKey(UserListFragment.class)
    abstract UserListFragment bindsUserListFragment();

}

Y el UserListModule proporciona dependencias para el fragmento. Algunas de las dependencias solo quiero vincular las instancias y devolverlas, como

 @Binds
 @ActivityScope
 abstract UserListView mUserListView(UserListFragment userListFragment);

En lugar de simplemente devolver la dependencia, como

@Provides
@ActivityScope
UserListView mUserListView(UserListFragment userListFragment){
    return userListFragment;
}

Mi módulo contiene algunos @Provides métodos también. ¿Podemos usar ambos? @Binds y @Provides métodos en el mismo módulo? Intenté como se muestra a continuación

        @Module
        public abstract class UserListModule {
            @Provides
            @ActivityScope
            UserListFragment mUserListFragment() {
                return new UserListFragment();
            }

            @Binds
            @ActivityScope
            abstract UserListView mUserListView(UserListFragment userListFragment);

           // other provides and binds methods...
           ......
           .....

        }

Y es un error de lanzamiento

Error:(22, 8) error: dagger.internal.codegen.ComponentProcessor was unable to process this interface because not all of its dependencies could be resolved. Check for compilation errors or a circular dependency with generated code.

¿Hay alguna manera de hacer esto?

  • Te das cuenta de que los fragmentos que agregas al administrador de fragmentos serán recreados por el sistema y probablemente no estarán en el Módulo después de la muerte del proceso, ¿verdad?

    – EpicPandaForce

    7 de octubre de 2017 a las 9:47

  • ¿Querías decir que no debería Scope el fragmento? ¿Puedes ser más claro, por favor? no te entendí bien

    usuario4260260

    7 de octubre de 2017 a las 9:51

  • Después de la muerte de un proceso, el fragmento será inicializado por el sistema y no por este módulo. Por lo tanto, es probable que termine con 2 instancias.

    – EpicPandaForce

    7 de octubre de 2017 a las 9:58

  • Está bien, pero ¿qué pasa con la pregunta real? ¿Podemos usar los métodos Binds y Provides en el mismo módulo?

    usuario4260260

    7 oct 2017 a las 10:06

  • Creo @Binds + @Provides debería funcionar, pero no sé cómo ContributesAndroidInjector lo afecta

    – EpicPandaForce

    7 de octubre de 2017 a las 10:08

@Binds y @ContributesAndroidInjector los métodos deben ser abstractos, porque no tienen cuerpos de método. Eso significa que deben ir a una interfaz o clase abstracta. @Provides los métodos pueden ser staticlo que significa que pueden utilizar clases abstractas e interfaces compiladas con Java-8, pero no estáticas (“instancia”) @Provides los métodos no funcionan en clases abstractas. Esto se enumera explícitamente en las preguntas frecuentes de Dagger, en las secciones “¿Por qué no puedo @Binds e instancia @Provides métodos van en el mismo módulo?” y “¿Qué hago en su lugar?”.

Si tu @Provides el método no usa el estado de la instancia, puede marcarlo staticy puede ir a una clase abstracta adyacente a su @Binds métodos. Si no, considere poner los enlaces como @Binds y @ContributesAndroidInjector en una clase separada, posiblemente una clase anidada estática, e incluyendo eso usando el includes atributo en Dagger’s @Module anotación.

Una pequeña adición a la solución de Jeff anterior:

puede crear una interfaz interna en lugar de una clase interna estática, como esta:

@Module(includes = AppModule.BindsModule.class)
public class AppModule {
    // usual non-static @Provides
    @Provides
    @Singleton
    Checkout provideCheckout(Billing billing, Products products) {
        return Checkout.forApplication(billing, products);
    }
    // interface with @Binds
    @Module
    public interface BindsModule {
        @Binds
        ISettings bindSettings(Settings settings);
    }
}

  • Además de eliminar la palabra clave abstracta repetitiva, ¿hay alguna ventaja/desventaja en usar una interfaz en lugar de una clase abstracta?

    – Pablo T.

    6 de junio de 2019 a las 9:05

  • Además de eliminar la palabra clave abstracta repetitiva, ¿hay alguna ventaja/desventaja en usar una interfaz en lugar de una clase abstracta?

    – Pablo T.

    6 de junio de 2019 a las 9:07

Avatar de usuario de Levon Petrosyan
levon petrosyan

En kotlin, puedes aprovechar objeto compañero

@Module
abstract class MyDaggerModule {

    @Binds
    abstract fun provideSomething(somethingImpl: SomethingImpl): Something

    companion object {

        @Provides
        fun provideAnotherThing(): AnotherThing {
            return AnotherThing()
        }
    }
}

  • ¿No funciona con encuadernación múltiple?

    – DEVS bit a bit

    21 de febrero de 2022 a las 16:44

avatar de usuario de jamescodingnow
jamescodificando ahora

Esta es otra solución de tipo: agregue módulos a otro módulo después de eso, puede llamar al módulo superior en la interfaz de su componente. Puede ser más eficiente porque puede usar abstracto y estático.

Los detalles y ejemplos se encuentran a continuación:

Por ejemplo, tenemos una interfaz de componentes y dos módulos como ComponentClasses, Módulo_ClaseA y Módulo_ClaseB.

Módulo_ClaseA es:

@Module
public class Module_ClassA {

   @Provides
   static ClassA provideClassA(){

     return new ClassA();
   }
}

Módulo_ClaseB es:

@Module
abstract class Module_ClassB {

   @Binds
   abstract ClassB bindClassB(Fragment fragment); //Example parameter
}

Así que ahora, tenemos dos modelos. Si desea usarlos juntos, puede agregar uno de ellos al otro. Por ejemplo: puede agregar Módulo_ClaseB a Módulo_ClaseA:

@Module(includes = Module_ClassB.class)
public class Module_ClassA {

   @Provides
   static ClassA provideClassA(){

     return new ClassA();
   }
}

Finalmente, no necesita agregar ambos módulos a su clase de componente. Solo puede agregar su módulo superior en su clase de componente, así:

ComponentClasses es:

@Component(modules = Module_ClassA)
public interface ComponentClasses {

   //Example code 
   ArrayList<CustomModel> getList();

}

Sin embargo, debe tener cuidado porque necesita agregar su módulo superior. Por lo tanto, Module_ClassA se agregó en la interfaz ComponentClasses.

¿Ha sido útil esta solución?