¿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 .

 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

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 y Fragment subclases: en onCreate
  • Subclases BroadcastReceiver (incluye, por ejemplo, AppWidgetProvider ): en onReceive

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.

  • Dagger 2 Inyecciones estáticas
  • ¿Cómo inyectar la dependencia al fragmento androide anidado?
  • Android Dagger actualiza los valores fuera del módulo
  • Obtener la actividad actual de Application.Context - MonoAndroid
  • Alcance y subcomponentes de Dagger 2
  • ¿Hay algún marco de aplicación de Android como primavera?
  • Daga 2 - dos proporciona el método que proporcionan la misma interfaz
  • Dagger 2 en Android: inyectar la misma dependencia en Activity y retener Fragment
  • No se puede inyectar valor nulo en la clase
  • ¿Cómo se compara Transfuse con Dagger?
  • Cómo inyectar el contexto usando RoboGuice en Android?
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.