¿Cómo configurar la dependency injection DAGGER desde cero en el proyecto de Android?

Cómo usar Dagger? ¿Cómo configurar Dagger para trabajar en mi proyecto de Android?

Me gustaría utilizar Dagger en mi proyecto de Android, pero me parece confuso.

EDITAR: Dagger2 también salió desde 2015 04 15, ¡y es aún más confuso!

[Esta pregunta es un “apéndice” en el que estoy agregando mi respuesta a medida que aprendí más sobre Dagger1, y aprendo más sobre Dagger2. Esta pregunta es más una guía que una “pregunta”.]

Guía para Dagger 2.x (Edición revisada 6) :

Los pasos son los siguientes:

1.) agregue Dagger a sus archivos build.gradle :

  • nivel superior build.gradle :

.

 // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { repositories { jcenter() } dependencies { classpath 'com.android.tools.build:gradle:2.2.0' classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' //added apt for source code generation } } allprojects { repositories { jcenter() } } 
  • nivel de la aplicación build.gradle :

.

 apply plugin: 'com.android.application' apply plugin: 'com.neenbedankt.android-apt' //needed for source code generation android { compileSdkVersion 24 buildToolsVersion "24.0.2" defaultConfig { applicationId "your.app.id" minSdkVersion 14 targetSdkVersion 24 versionCode 1 versionName "1.0" } buildTypes { debug { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } dependencies { apt 'com.google.dagger:dagger-compiler:2.7' //needed for source code generation compile fileTree(dir: 'libs', include: ['*.jar']) compile 'com.android.support:appcompat-v7:24.2.1' compile 'com.google.dagger:dagger:2.7' //dagger itself provided 'org.glassfish:javax.annotation:10.0-b28' //needed to resolve comstacktion errors, thanks to tutplus.org for finding the dependency } 

2.) Cree su clase AppContextModule que proporciona las dependencias.

 @Module //a module could also include other modules public class AppContextModule { private final CustomApplication application; public AppContextModule(CustomApplication application) { this.application = application; } @Provides public CustomApplication application() { return this.application; } @Provides public Context applicationContext() { return this.application; } @Provides public LocationManager locationService(Context context) { return (LocationManager) context.getSystemService(Context.LOCATION_SERVICE); } } 

3.) crear la clase AppContextComponent que proporciona la interfaz para obtener las clases que son inyectables.

 public interface AppContextComponent { CustomApplication application(); //provision method Context applicationContext(); //provision method LocationManager locationManager(); //provision method } 

3.1.) Así es como crearías un módulo con una implementación:

 @Module //this is to show that you can include modules to one another public class AnotherModule { @Provides @Singleton public AnotherClass anotherClass() { return new AnotherClassImpl(); } } @Module(includes=AnotherModule.class) //this is to show that you can include modules to one another public class OtherModule { @Provides @Singleton public OtherClass otherClass(AnotherClass anotherClass) { return new OtherClassImpl(anotherClass); } } public interface AnotherComponent { AnotherClass anotherClass(); } public interface OtherComponent extends AnotherComponent { OtherClass otherClass(); } @Component(modules={OtherModule.class}) @Singleton public interface ApplicationComponent extends OtherComponent { void inject(MainActivity mainActivity); } 

Tenga cuidado:: debe proporcionar la anotación @Scope (como @Singleton o @ActivityScope ) en el método anotado @Provides del módulo para obtener un proveedor de ámbito dentro del componente generado; de lo contrario, no se realizará un cocimiento y obtendrá una nueva instancia cada vez que se inyecta.

3.2.) Cree un componente con ámbito de aplicación que especifique lo que puede inyectar (esto es lo mismo que injects={MainActivity.class} en Dagger 1.x):

 @Singleton @Component(module={AppContextModule.class}) //this is where you would add additional modules, and a dependency if you want to subscope public interface ApplicationComponent extends AppContextComponent { //extend to have the provision methods void inject(MainActivity mainActivity); } 

3.3.) Para las dependencias que puede crear a través de un constructor usted mismo y no quiere redefinir usando un @Module (por ejemplo, usa sabores de construcción en su lugar para cambiar el tipo de implementación), puede usar @Inject constructor anotado.

 public class Something { OtherThing otherThing; @Inject public Something(OtherThing otherThing) { this.otherThing = otherThing; } } 

Además, si usa @Inject constructor, puede usar la inyección de campo sin tener que llamar explícitamente a component.inject(this) :

 public class Something { @Inject OtherThing otherThing; @Inject public Something() { } } 

Estas clases de constructor @Inject se agregan automáticamente al componente del mismo ámbito sin tener que especificarlas explícitamente en un módulo.

Se @Singleton @Inject clase de constructor con @Inject @Singleton @Inject en los componentes de ámbito @Singleton .

 @Singleton // scoping public class Something { OtherThing otherThing; @Inject public Something(OtherThing otherThing) { this.otherThing = otherThing; } } 

3.4.) Después de que haya definido una implementación específica para una interfaz determinada, haga lo siguiente:

 public interface Something { void doSomething(); } @Singleton public class SomethingImpl { @Inject AnotherThing anotherThing; @Inject public SomethingImpl() { } } 

Tendrá que “enlazar” la implementación específica a la interfaz con un @Module .

 @Module public class SomethingModule { @Provides Something something(SomethingImpl something) { return something; } } 

Una mano corta para esto desde Dagger 2.4 es la siguiente:

 @Module public abstract class SomethingModule { @Binds abstract Something something(SomethingImpl something); } 

4.) crear una clase de Injector para manejar su componente de nivel de aplicación (reemplaza el ObjectGraph monolítico)

(Nota: Rebuild Project para crear la clase de constructor DaggerApplicationComponent usando APT)

 public enum Injector { INSTANCE; ApplicationComponent applicationComponent; private Injector(){ } static void initialize(CustomApplication customApplication) { ApplicationComponent applicationComponent = DaggerApplicationComponent.builder() .appContextModule(new AppContextModule(customApplication)) .build(); INSTANCE.applicationComponent = applicationComponent; } public static ApplicationComponent get() { return INSTANCE.applicationComponent; } } 

5.) crea tu clase CustomApplication

 public class CustomApplication extends Application { @Override public void onCreate() { super.onCreate(); Injector.initialize(this); } } 

6.) agregue CustomApplication a su AndroidManifest.xml .

  

7.) Inyecta tus clases en MainActivity

 public class MainActivity extends AppCompatActivity { @Inject CustomApplication customApplication; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Injector.get().inject(this); //customApplication is injected from component } } 

8.) Disfruta!

+1.) Puede especificar el Scope de sus componentes con el que puede crear componentes de ámbito de actividad . Los substratos le permiten proporcionar dependencias que solo necesita para un subámbito dado, en lugar de a lo largo de toda la aplicación. Normalmente, cada actividad obtiene su propio módulo con esta configuración. Tenga en cuenta que existe un proveedor de ámbito por componente , lo que significa que para conservar la instancia para esa actividad, el componente en sí debe sobrevivir al cambio de configuración. Por ejemplo, podría sobrevivir a través onRetainCustomNonConfigurationInstance() , o un scope de mortero.

Para obtener más información sobre subcobertura, consulte la guía de Google . También consulte este sitio sobre métodos de provisión y también la sección de dependencias de componentes ) y aquí .

Para crear un ámbito personalizado, debe especificar la anotación de calificador de ámbito:

 @Scope @Retention(RetentionPolicy.RUNTIME) public @interface YourCustomScope { } 

Para crear un subámbito, debe especificar el scope en su componente y especificar ApplicationComponent como su dependencia. Obviamente, también debe especificar el subámbito en los métodos del proveedor del módulo.

 @YourCustomScope @Component(dependencies = {ApplicationComponent.class}, modules = {CustomScopeModule.class}) public interface YourCustomScopedComponent extends ApplicationComponent { CustomScopeClass customScopeClass(); void inject(YourScopedClass scopedClass); } 

Y

 @Module public class CustomScopeModule { @Provides @YourCustomScope public CustomScopeClass customScopeClass() { return new CustomScopeClassImpl(); } } 

Tenga en cuenta que solo se puede especificar un componente de ámbito como una dependencia. Piénselo exactamente como la herencia múltiple no es compatible con Java.

+2.) Acerca de @Subcomponent : esencialmente, un @Subcomponent puede reemplazar una dependencia de componente; pero en lugar de usar un generador provisto por el procesador de anotación, necesitaría usar un método de fábrica de componentes.

Así que esto:

 @Singleton @Component public interface ApplicationComponent { } @YourCustomScope @Component(dependencies = {ApplicationComponent.class}, modules = {CustomScopeModule.class}) public interface YourCustomScopedComponent extends ApplicationComponent { CustomScopeClass customScopeClass(); void inject(YourScopedClass scopedClass); } 

Se convierte en esto:

 @Singleton @Component public interface ApplicationComponent { YourCustomScopedComponent newYourCustomScopedComponent(CustomScopeModule customScopeModule); } @Subcomponent(modules={CustomScopeModule.class}) @YourCustomScope public interface YourCustomScopedComponent { CustomScopeClass customScopeClass(); } 

Y esto:

 DaggerYourCustomScopedComponent.builder() .applicationComponent(Injector.get()) .customScopeModule(new CustomScopeModule()) .build(); 

Se convierte en esto:

 Injector.INSTANCE.newYourCustomScopedComponent(new CustomScopeModule()); 

+3.): Compruebe también otras preguntas sobre desbordamiento de stack con respecto a Dagger2, proporcionan mucha información. Por ejemplo, mi estructura Dagger2 actual se especifica en esta respuesta .

Gracias

Gracias por las guías en Github , TutsPlus , Joe Steele , Froger MCS y Google .

También para esta guía de migración paso a paso que encontré después de escribir esta publicación.

Y para la explicación del scope por Kirill.

Aún más información en la documentación oficial .

Guía para Dagger 1.x :

Los pasos son los siguientes:

1.) agregue Dagger al archivo build.gradle para las dependencias

 dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) ... compile 'com.squareup.dagger:dagger:1.2.2' provided 'com.squareup.dagger:dagger-compiler:1.2.2' 

Además, agregue packaging-option para evitar un error sobre duplicate APKs .

 android { ... packagingOptions { // Exclude file to avoid // Error: Duplicate files during packaging of APK exclude 'META-INF/services/javax.annotation.processing.Processor' } } 

2.) crear una clase de Injector para manejar el ObjectGraph .

 public enum Injector { INSTANCE; private ObjectGraph objectGraph = null; public void init(final Object rootModule) { if(objectGraph == null) { objectGraph = ObjectGraph.create(rootModule); } else { objectGraph = objectGraph.plus(rootModule); } // Inject statics objectGraph.injectStatics(); } public void init(final Object rootModule, final Object target) { init(rootModule); inject(target); } public void inject(final Object target) { objectGraph.inject(target); } public  T resolve(Class type) { return objectGraph.get(type); } } 

3.) Cree un RootModule para unir sus futuros módulos. Tenga en cuenta que debe incluir injects para especificar cada clase en la que @Inject anotación @Inject , porque de lo contrario Dagger arroja RuntimeException .

 @Module( includes = { UtilsModule.class, NetworkingModule.class }, injects = { MainActivity.class } ) public class RootModule { } 

4.) En caso de que tenga otros submódulos dentro de sus módulos especificados en su Root, cree módulos para ellos:

 @Module( includes = { SerializerModule.class, CertUtilModule.class } ) public class UtilsModule { } 

5.) crear los módulos de hoja que reciben las dependencias como parámetros de constructor. En mi caso, no había una dependencia circular, así que no sé si Dagger puede resolver eso, pero me parece poco probable. Los parámetros del constructor también se deben proporcionar en un Módulo por Dagger, si especifica complete = false entonces también puede estar en otros Módulos.

 @Module(complete = false, library = true) public class NetworkingModule { @Provides public ClientAuthAuthenticator providesClientAuthAuthenticator() { return new ClientAuthAuthenticator(); } @Provides public ClientCertWebRequestor providesClientCertWebRequestor(ClientAuthAuthenticator clientAuthAuthenticator) { return new ClientCertWebRequestor(clientAuthAuthenticator); } @Provides public ServerCommunicator providesServerCommunicator(ClientCertWebRequestor clientCertWebRequestor) { return new ServerCommunicator(clientCertWebRequestor); } } 

6.) Extienda la Application e inicialice el Injector .

 @Override public void onCreate() { super.onCreate(); Injector.INSTANCE.init(new RootModule()); } 

7.) En su MainActivity , llame al inyector en el método onCreate() .

 @Override protected void onCreate(Bundle savedInstanceState) { Injector.INSTANCE.inject(this); super.onCreate(savedInstanceState); ... 

8.) Use @Inject en su MainActivity .

 public class MainActivity extends ActionBarActivity { @Inject public ServerCommunicator serverCommunicator; ... 

Si obtiene el error, no injectable constructor found , asegúrese de no haber olvidado las anotaciones @Provides .