¿Cómo configurar la inyección de dependencia con Dagger para otras cosas que no sean Actividades y Fragmentos?
Empecé a configurar la inyección de dependencia con Dagger de la siguiente manera. Por favor, siéntase alentado a corregir mi implementación ya que podría tener errores allí! La implementación sigue el ejemplo simple de Android proporcionado por el proyecto. A continuación se puede ver cómo agregé con éxito la inyección de dependencia para Activities
y Fragments
. Trato de mantenerlo fácil por ahora, así que decidí inyectar Timber como una sustitución de logger para la utilidad de registro de Android .
import android.app.Application; import java.util.Arrays; import java.util.List; import dagger.ObjectGraph; import com.example.debugging.LoggingModule; public class ExampleApplication extends Application { private ObjectGraph mObjectGraph; protected List<Object> getModules() { return Arrays.asList( new AndroidModule(this), new ExampleModule(), new LoggingModule() ); } private void createObjectGraphIfNeeded() { if (mObjectGraph == null) { Object[] modules = getModules().toArray(); mObjectGraph = ObjectGraph.create(modules); } } public void inject(Object object) { createObjectGraphIfNeeded(); mObjectGraph.inject(object); } }
Por ahora el AndroidModule
no se utiliza en ningún lugar pero puede ser útil cuando se necesita un Context
y LayoutInflater
por ejemplo en CursorAdapters
.
- Inyectar objetos de objetos en Class Util (usando Dagger)
- Dagger 2 - ¿Por qué es un ciclo de dependencia?
- Los objetos inyectados se convirtieron en nulos después de actualizar a Roboguice 3
- Dagger gráfico de actividad y módulos separados
- Uso de Dagger 2 para inyectar en servicio
import android.content.Context; import android.view.LayoutInflater; import javax.inject.Singleton; import dagger.Module; import dagger.Provides; /** * A module for Android-specific dependencies which require a {@link Context} * or {@link android.app.Application} to create. */ @Module(library = true) public class AndroidModule { private final ExampleApplication mApplication; public AndroidModule(ExampleApplication application) { mApplication = application; } /** * Allow the application context to be injected but require that it be * annotated with {@link ForApplication @Annotation} to explicitly * differentiate it from an activity context. */ @Provides @Singleton @ForApplication Context provideApplicationContext() { return mApplication; } @Provides @Singleton LayoutInflater provideLayoutInflater() { return (LayoutInflater) mApplication .getSystemService(Context.LAYOUT_INFLATER_SERVICE); } }
No estoy seguro de lo que los proveedores específicos de la aplicación iría aquí. Me quedo con la registración por ahora.
import dagger.Module; @Module( complete = false ) public class ExampleModule { public ExampleModule() { // TODO put your application-specific providers here! } }
Preparé LoggingModule
que proporciona acceso a Timber .
package com.example.debugging; import javax.inject.Singleton; import dagger.Module; import dagger.Provides; import com.example.BuildConfig; import com.example.activities.BaseFragmentActivity; import com.example.activities.DetailsActivity; import com.example.fragments.BaseListFragment; import com.example.fragments.ProfilesListFragment; import timber.log.Timber; @Module(injects = { // Activities BaseFragmentActivity.class, DetailsActivity.class, // Fragments BaseListFragment.class, ProfilesListFragment.class }) public class LoggingModule { @Provides @Singleton Timber provideTimber() { return BuildConfig.DEBUG ? Timber.DEBUG : Timber.PROD; } }
La clase base de Activities
inyecta en el gráfico de objetos …
package com.example.activities; import android.os.Bundle; import com.actionbarsherlock.app.SherlockFragmentActivity; import javax.inject.Inject; import com.example.ExampleApplication; import timber.log.Timber; public abstract class BaseFragmentActivity extends SherlockFragmentActivity { @Inject Timber mTimber; @Override protected void onCreate(Bundle savedInstanceState) { // ... super.onCreate(savedInstanceState); ((ExampleApplication) getApplication()).inject(this); } }
… y cualquier sub clase beneficia de que Timber esté presente.
package com.example.activities; import android.os.Bundle; import com.example.R; public class DetailsActivity extends BaseFragmentActivity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_details); mTimber.i("onCreate"); // ... } }
Lo mismo para Fragments
: la clase base hace el trabajo sucio …
package com.example.fragments; import android.os.Bundle; import com.actionbarsherlock.app.SherlockListFragment; import javax.inject.Inject; import com.example.ExampleApplication; import timber.log.Timber; public abstract class BaseListFragment extends SherlockListFragment { @Inject Timber mTimber; @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); ((ExampleApplication) getActivity().getApplication()).inject(this); } }
… y la sub clase se beneficia de su superclase.
package com.example.fragments; import android.os.Bundle; public class ProfilesListFragment extends BaseListFragment { @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); // TODO This might be a good example to inject resources // in the base class. But how? setEmptyText(getResources() .getString(R.string.profiles_list_no_content)); mTimber.i("onActivityCreated"); // ... } }
Hasta aquí todo bien. Pero, ¿cómo puede inyectar Timber en BaseCursorAdapter
, BaseContentProvider
, BaseSQLiteOpenHelper
, BaseService
, BaseAsyncTask
y métodos auxiliares static
?
El ejemplo androide obsoleto de Christopher Perry señala cómo inyectar un adaptador en un ListFragment pero no cómo inyectar Context
, Resources
, LayoutInflater
, Cursor
en el (Cursor)Adapter
o simplemente en Timber .
Referencias:
- Daga
- Android-simple ejemplo
- Jesse Wilson – Dagger: un inyector de dependencia rápida para Android y Java
- Eric Burke – Anatomía de la aplicación para Android
- Inyección de Butterknife View
- Prueba funcional de Android con Dagger
- Lazy Injection con Dagger 2 en Android
- Dagger and Butter Knife vs Android Annotations
- Sitio Web MVC que llama a WCF es mejor o MVC que llama a la operación de base de datos directamente es mejor
- OkHttp Interceptor utilizando OkHttpClient sin ciclo de dependencia
- Aplicación para Android con RoboGuice 2.0 - Cómo inyectar un singleton con el contexto de la aplicación
- @Named proveedores con los mismos tipos de retorno terminan dando java.lang.IllegalArgumentException: Duplicar
Echa un vistazo a los ejemplos de Andy Dennie para inyectarse en diferentes escenarios:
https://github.com/adennie/fb-android-dagger
Algunos puntos en los que me inyecto:
-
Activity
,Service
yFragment
subclases: enonCreate
- Subclases
BroadcastReceiver
(incluye, por ejemplo,AppWidgetProvider
): enonReceive
Tl; dr Hay mucho que hacer en esta pregunta, pero podría ser ayudado con un Gist de la mía . Si puedes obtener un Context
algún lugar, puedes inyectarlo ObjectGraph
el ObjectGraph
mantenido por tu clase Application
.
Editar por JJD :
El ObjectGraph
en el Gist se puede integrar de la siguiente manera:
public class ExampleApplication extends Application implements ObjectGraph.ObjectGraphApplication { /* Application Lifecycle */ @Override public void onCreate() { // Creates the dependency injection object graph _object_graph = ObjectGraph.create(...); } /* ObjectGraphApplication Contract */ @Override public void inject(@Nonnull Object dependent) { _object_graph.inject(dependent); } /** Application's object graph for handling dependency injection */ private ObjectGraph _object_graph; }
…
public abstract class BaseFragmentActivity extends SherlockFragmentActivity { @Inject Timber _timber; @Override protected void onCreate(Bundle icicle) { super.onCreate(icicle); ObjectGraph.inject(this); } }
…
public abstract class BaseListFragment extends SherlockListFragment { @Inject Timber _timber; @Override public void onActivityCreated(Bundle icicle) { super.onActivityCreated(icicle); ObjectGraph.inject(this); } }
Trabajos similares para BaseCursorAdapter
, BaseContentProvider
y BaseService
.
Tuvimos que hacer esto mismo (es decir, inyectar un registrador en todas partes). Terminamos haciendo una envoltura estática muy pequeña (por lo tanto, disponible en todas partes) que envuelve una clase que se inyecta a través de la daga de forma estática.
package com.example.secret; import javax.inject.Inject; import com.example.interfaces.Logger; public class LoggerProvider { @Inject static Logger logger; public LoggerProvider() { } public static Logger getLogger() { return logger; }
}
Logger implementa su interfaz de registro. Para inyectarlo en el nivel de aplicación que necesita:
graph = ObjectGraph.create(getModules().toArray()); graph.injectStatics();
Código aquí: https://github.com/nuria/android-examples/tree/master/dagger-logger-example
Inyección de Context
, Resources
y LayoutInflater
(pasando el contexto de la aplicación cuando haces esto en Aplicación).
@Module(complete = false) public class AndroidServicesModule { private final Context context; public AndroidServicesModule(@ForApplication Context context) { this.context = context; } @Provides @Singleton Resources provideResources() { return context.getResources(); } @Provides @Singleton LocationManager provideLocationManager() { return (LocationManager) context.getSystemService(Context.LOCATION_SERVICE); } @Provides @Singleton LayoutInflater provideLayoutInflater() { return (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); } @Provides @Singleton Resources provideResources() { return context.getResources(); } @Provides @ForApplication Context provideContext() { return context; } }
Por supuesto, probablemente debería calificar el contexto con una anotación especificando su contexto de aplicación (por ejemplo, @ForApplication
).
Si necesitas el equivalente de @InjectResource de @InjectResource(int)
no puedo pensar en nada de improviso. Butterknife parece ser el derecho lib para añadir esto. Vea aquí
Puede agregar estas construcciones a la clase ExampleApplication
:
private static ExampleApplication INSTANCE; @Override public void onCreate() { super.onCreate(); INSTANCE = this; mObjectGraph = ObjectGraph.create(getModules()); mObjectGraph.injectStatics(); mObjectGraph.inject(this); } public static ExampleApplication get() { return INSTANCE; }
Después de que usted es capaz de inyectar cualquier objeto (denotado por this
) con una línea simple:
ExampleApplication.get().inject(this)
Esto debe llamarse en constructor para objetos que cree manualmente o onCreate
(o análogo) para aquellos que son administrados por Android System.
- Cómo evitar un brindis si ya se muestra un brindis
- ¿Cómo salir de una aplicación de Android con código?