tutorial español dagger2 android dependency-injection android-asynctask android-contentprovider dagger

android - dagger2 - dagger 2 tutorial español



¿Cómo configurar la inyección de dependencia usando Dagger para cosas que no sean Actividades y Fragmentos? (5)

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 eso, puedes inyectar cualquier objeto (indicado por this ) con una simple línea:

ExampleApplication.get().inject(this)

Esto se debe llamar en el constructor para los objetos que crea manualmente o onCreate (o análogo) para aquellos que son administrados por el sistema Android.

Comencé a configurar la inyección de dependencia usando Dagger de la siguiente manera. ¡Por favor, siéntete animado a corregir mi implementación ya que puedo tener errores allí! La implementación sigue el ejemplo simple de Android proporcionado por el proyecto. A continuación, puede ver cómo agregué con éxito la inyección de dependencia para Activities y Fragments . Trato de mantenerlo tranquilo por ahora, así que decidí inyectar Timber como una sustitución de registrador 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 usa en ninguna parte, pero podría ser útil cuando se necesita un Context and 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 qué proveedores de aplicaciones específicas irían aquí. Me quedo con el registro 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 para 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 subclase se beneficia de que Timber ya 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 subclase se beneficia de su súper clase.

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 se puede inyectar Timber en BaseCursorAdapter , BaseContentProvider , BaseSQLiteOpenHelper , BaseService , BaseAsyncTask y métodos static ayuda?

El ejemplo de Android 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 solo Timber .

Referencias


Tuvimos que hacer lo mismo (es decir, inyectar un registrador en todas partes). Terminamos haciendo una envoltura estática muy pequeña (disponible en todas partes) que envuelve una clase que se inyecta mediante daga estáticamente.

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 necesita:

graph = ObjectGraph.create(getModules().toArray()); graph.injectStatics();

Codifique 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 lo actualizas en la 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 que es el contexto de la aplicación (por ejemplo, @ForApplication ).

Si necesita el equivalente de @InjectResource @InjectResource(int) de Roboguice, no puedo pensar en nada al azar. Butterknife parece ser la lib derecha para agregar esto a. Mira aquí


Vea los ejemplos de Andy Dennie para inyectar en diferentes escenarios:

https://github.com/adennie/fb-android-dagger

Algunos puntos donde me inyecto:

  • Subclases Activity , Service y Fragment : en onCreate
  • Subclases de BroadcastReceiver (incluye, por ejemplo, AppWidgetProvider ): en onReceive

tl; dr Están pasando muchas cosas en esta pregunta, pero podría ser útil con una idea fundamental . Si puede obtener un Context algún lugar, puede inyectarlo en función del ObjectGraph mantiene su clase de Application .

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