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
yFragment
: enonCreate
- Subclases de
BroadcastReceiver
(incluye, por ejemplo,AppWidgetProvider
): enonReceive
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
.