para - ¿Cómo configurar la inyección de dependencia DAGGER desde cero en un proyecto de Android?
dagger 2 tutorial español (3)
Aquí puede encontrar útiles ejemplos de proyectos y tutoriales de Dagger2.
¿Cómo usar Dagger? ¿Cómo configurar Dagger para que funcione en mi proyecto de Android?
Me gustaría usar Dagger en mi proyecto de Android, pero me resulta confuso.
EDITAR: Dagger2 también está disponible desde 2015 04 15, ¡y es aún más confuso!
[Esta pregunta es un "trozo" en el que estoy agregando a mi respuesta a medida que aprendí más sobre Dagger1, y aprendí más sobre Dagger2. Esta pregunta es más una guía que una "pregunta".]
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.) crea una clase
Injector
para manejar
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> T resolve(Class<T> type)
{
return objectGraph.get(type);
}
}
3.) Cree un
RootModule
para vincular sus futuros módulos.
Tenga en cuenta que debe incluir
injects
para especificar cada clase en la que utilizará la 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 aquellos:
@Module(
includes = {
SerializerModule.class,
CertUtilModule.class
}
)
public class UtilsModule
{
}
5.) crea los módulos hoja que reciben las dependencias como parámetros de constructor.
En mi caso, no había dependencia circular, por lo que no sé si Dagger puede resolver eso, pero me parece poco probable.
Dagger también debe proporcionar los parámetros del constructor en un Módulo; si especifica
complete = false
, 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.) Usa
@Inject
en tu
MainActivity
.
public class MainActivity extends ActionBarActivity
{
@Inject
public ServerCommunicator serverCommunicator;
...
Si obtiene el error
no injectable constructor found
, asegúrese de no olvidar las anotaciones
@Provides
.
Guía para Dagger 2.x (Edición revisada 6) :
Los pasos son los siguientes:
1.)
agrega
Dagger
a tus 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 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 compilation 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.)
cree 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ía 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);
}
Cuidado:
debe proporcionar la anotación
@Scope
(como
@Singleton
o
@ActivityScope
) en el método anotado
@Provides
del módulo para obtener un proveedor con alcance dentro de su componente generado; de lo contrario, no tendrá ámbito y obtendrá una nueva instancia cada vez que inyectas.
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 querrá redefinir usando un
@Module
(por ejemplo, usa sabores de compilación para cambiar el tipo de implementación), puede usar el constructor anotado
@Inject
.
public class Something {
OtherThing otherThing;
@Inject
public Something(OtherThing otherThing) {
this.otherThing = otherThing;
}
}
Además, si usa el constructor
@Inject
, 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
@Singleton
ámbito
@Inject
en los componentes con ámbito
@Singleton
.
@Singleton // scoping
public class Something {
OtherThing otherThing;
@Inject
public Something(OtherThing otherThing) {
this.otherThing = otherThing;
}
}
3.4.) Después de haber definido una implementación específica para una interfaz determinada, así:
public interface Something {
void doSomething();
}
@Singleton
public class SomethingImpl {
@Inject
AnotherThing anotherThing;
@Inject
public SomethingImpl() {
}
}
Deberá "vincular" la implementación específica a la interfaz con un
@Module
.
@Module
public class SomethingModule {
@Provides
Something something(SomethingImpl something) {
return something;
}
}
Una abreviatura para esto desde Dagger 2.4 es la siguiente:
@Module
public abstract class SomethingModule {
@Binds
abstract Something something(SomethingImpl something);
}
4.)
cree una clase
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
.
<application
android:name=".CustomApplication"
...
7.)
Inyecte sus 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 con ámbito de actividad
.
Los subscopios le permiten proporcionar dependencias que solo necesita para un subscopio dado, en lugar de en toda la aplicación.
Por lo general, cada actividad obtiene su propio módulo con esta configuración.
Tenga en cuenta que existe un proveedor con alcance
por componente
, lo que significa que para retener la instancia para esa actividad, el componente en sí debe sobrevivir al cambio de configuración.
Por ejemplo, podría sobrevivir a través de
onRetainCustomNonConfigurationInstance()
, o un alcance de Mortar.
Para obtener más información sobre el subscoping, consulte la guía de Google . Consulte también este sitio sobre métodos de suministro y también la sección de dependencias de componentes ) y here .
Para crear un ámbito personalizado, debe especificar la anotación del calificador de ámbito:
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface YourCustomScope {
}
Para crear un subscopio, debe especificar el alcance de su componente y especificar
ApplicationComponent
como su dependencia.
Obviamente, también debe especificar el subscopio 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 dependencia. Piénselo exactamente como no se admite la herencia múltiple en Java.
+2.)
Acerca de
@Subcomponent
: esencialmente, un
@Subcomponent
alcance puede reemplazar una dependencia de componente;
pero en lugar de usar un generador proporcionado por el procesador de anotaciones, 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 de desbordamiento de pila con respecto a Dagger2, que proporcionan mucha información. Por ejemplo, mi estructura actual de Dagger2 se especifica en esta respuesta .
Gracias
Gracias por las guías en Github , code.tutsplus.com/tutorials/… , 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 alcance por Kirill.
Aún más información en la documentación oficial .