web-dev-qa-db-fra.com

Comment configurer l'injection de dépendance à l'aide de Dagger pour des choses autres que les activités et les fragments?

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:

28
JJD

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
8
orip

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.

5
Dandre Allison

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

2
Christopher Perry

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

2
nurieta

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.

0
colriot