J'ai commencé à configurer l'injection de dépendance en utilisant Dagger comme suit. N'hésitez pas à corriger mon implémentation car je pourrais y avoir des erreurs! L'implémentation suit le exemple simple d'Android fourni par le projet. Dans ce qui suit, vous pouvez voir comment j'ai réussi à ajouter l'injection de dépendances pour Activities
et Fragments
. J'essaie de garder les choses faciles pour le moment, j'ai donc décidé d'injecter Timber comme substitution de l'enregistreur pour tilisation du journal d'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);
}
}
À l'heure actuelle, le AndroidModule
n'est utilisé nulle part, mais peut être utile lorsqu'un Context
et LayoutInflater
est nécessaire, par exemple dans 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);
}
}
Je ne sais pas quels fournisseurs spécifiques à l'application iraient ici. Je reste avec la journalisation pour l'instant.
import dagger.Module;
@Module(
complete = false
)
public class ExampleModule {
public ExampleModule() {
// TODO put your application-specific providers here!
}
}
J'ai préparé LoggingModule
qui donne accès à 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 classe de base pour Activities
s'injecte dans le graphe objet ...
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);
}
}
... et toute sous-classe bénéficie de Bois déjà présent.
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");
// ...
}
}
Idem pour Fragments
: la classe de base fait le sale boulot ...
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);
}
}
... et la sous-classe bénéficie de sa super classe.
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");
// ...
}
}
Jusqu'ici tout va bien. Mais comment injecter du bois dans BaseCursorAdapter
, BaseContentProvider
, BaseSQLiteOpenHelper
, BaseService
, BaseAsyncTask
et static
méthodes d'assistance?
Le déprécié exemple Android de Christopher Perry montre comment injecter un Adapter dans un ListFragment mais pas comment injecter Context
, Resources
, LayoutInflater
, Cursor
dans le (Cursor)Adapter
ou simplement Bois .
Les références:
Découvrez les exemples d'Andy Dennie pour l'injection dans différents scénarios:
https://github.com/adennie/fb-Android-dagger
Quelques points où j'injecte:
Activity
, Service
et Fragment
sous-classes: dans onCreate
BroadcastReceiver
sous-classes (inclut, par exemple AppWidgetProvider
): dans onReceive
tl; dr Il y a beaucoup de choses dans cette question, mais cela pourrait être aidé avec un Gist of mine . Si vous pouvez obtenir un Context
quelque part, vous pouvez l'injecter en fonction de la ObjectGraph
gérée par votre classe Application
.
Modifier par JJD :
Le ObjectGraph
dans le Gist peut être intégré comme suit:
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);
}
}
Travaux similaires pour BaseCursorAdapter
, BaseContentProvider
et BaseService
.
Injection de Context
, Resources
et LayoutInflater
(en passant le contexte de l'application lorsque vous le créez dans Application).
@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;
}
}
Bien sûr, vous devriez probablement qualifier le contexte avec une annotation spécifiant que c'est le contexte de l'application (par exemple @ForApplication
).
Si vous avez besoin de l'équivalent de @InjectResource(int)
de Roboguice, je ne peux penser à rien du tout. Butterknife semble être la bonne bibliothèque à ajouter à cela. Voir ici
Nous avons dû faire la même chose (c'est-à-dire injecter un enregistreur partout). Nous avons fini par créer un très petit wrapper statique (donc disponible partout) qui encapsule une classe qui est injectée via dagger statiquement.
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 implémente votre interface de journalisation. Pour l'injecter au niveau de l'application, vous avez besoin de:
graph = ObjectGraph.create(getModules().toArray());
graph.injectStatics();
Code ici: https://github.com/nuria/Android-examples/tree/master/dagger-logger-example
Vous pouvez ajouter ces constructions à la classe 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;
}
Après cela, vous pouvez injecter n'importe quel objet (noté this
) avec une seule ligne:
ExampleApplication.get().inject(this)
Cela doit être appelé dans le constructeur pour les objets que vous créez manuellement ou onCreate
(ou analogique) pour ceux qui sont gérés par Android System.