infinite state machine

Module und Provider in Roboguice

Introduction

user

Franziska Neumeister

Hat "Media Systems" studiert, entwickelt mobile Apps und will wissen, ob Androiden auch von elektrischen Schafen träumen


LATEST POSTS

Multithreading mit RxJava 18th April, 2016

Testbaren Code schreiben (Teil 1) 23rd April, 2015

Android

Module und Provider in Roboguice

Posted on .

Dependency Injection Frameworks wie Roboguice trennen die Erzeugung von Objektgraphen vom Rest der Geschäftslogik. Oft reichen die Default-Einstellungen aus, damit Roboguice weiß, welche Klassen es verwenden soll und wie es Instanzen konstruiert.

Roboguice bietet die Möglichkeit, die Konstruktion von Objekten zu konfigurieren und verschiedene sogenannte Module anzulegen, die unterschiedliche Implementierungen von Objekt-Graphen bündeln.

Module

Roboguice injiziert standardmäßig eine Instanz vom Typ des zu injizierenden Attributs. Was passiert aber, wenn z.B. der Typ eines Attributes eine Schnittstelle ist? Module verbinden die (abstrakten) Typen, die mit @Inject für die Injektion markiert wurden mit konkreten Implementierungen. Das kann die Verbindung einer abstrakten Klasse oder einer Schnittstelle mit einer konkreten Klasse sein, die die Implementierung erfüllt oder das Ersetzen einer Klasse durch eine andere, die von ihr erbt.

public class BuildModule extends AbstractModule {
    private Application mAppContext;
    public BuildModule(Application appContext) {
        mAppContext = appContext;
    }

    @Override
    protected void configure() {
        // bindet eine Schnittstelle an eine konkrete Implementierung
        this.bind(ICombustible.class).to(Gasoline.class);
        // bindet eine Subklasse an eine Basisklasse
        this.bind(Engine.class).to(PetrolEngine.class);
    }
}

Damit Roboguice von der Existenz eines Moduls erfährt, muss es registriert werden. Das kann auf zwei Wegen geschehen. Über einen Tag in der AndroidManifest.xml Datei, der den vollständigen Namen der Modul-Klasse enthält, bzw. eine Liste der Modulnamen, die durch Kommata getrennt sind

<application ...>
    <meta-data android:name="roboguice.modules"
        android:value="de.infinitestatemachine.BuildModule" />
</application>

Der andere Weg ist programatisch durch die Methode getOrCreateBaseApplicationInjector(), die eine beliebige Anzahl an Modul-Instanzen als Argument akzeptiert. Unter den Modulen muss auch das DefaultRoboModule sein, sonst können keine Klassen aus dem Android SDK injiziert werden.

RoboGuice.getOrCreateBaseApplicationInjector(application, RoboGuice.DEFAULT_STAGE, RoboGuice.newDefaultRoboModule(application), new BuildModule(application));

Für den Einsatz bei UnitTests existiert noch die Methode overrideApplicationInjector(), die automatsich das DefaultRoboModule und alle Module aus der AndroidManifest.xml Datei lädt sowie zusätzlich als Argument übergebene Module speziell für die Testumgebung.

RoboGuice.overrideApplicationInjector(Robolectric.application, new TestModule());

Provider

Provider sind quasi Fabriken, die Roboguice benutzen kann, um Objekte zu konstruieren, die es sonst nicht automatisch erzeugen könnte. Das kann z.B. nötig sein wenn Objekte aus einer Bibliothek verwendet werden, die erst manuell initialisiert werden müssen, weil Roboguice dessen Abhängigkeiten nicht kennen kann ohne die entsprechenden Annotationen zur Kennzeichnung.
Ein Provider implementiert die Schnittstelle Provider und hat eine Methode get(), die eine neue Instanz des gewünschten Typs liefert

public class CombustibleProvider implements Provider {
    @Override
    public ICombustible get() {
        return new Gasoline();
    }
}

Dann muss nur noch im Modul der entsprechende Typ an den Provider gebunden werden:

public class BuildModule extends AbstractModule {
    @Override
    protected void configure() {
        // bindet eine Schnittstelle an einen Provider
        this.bind(ICombustible.class)
           .toProvider(CombustibleProvider);
    }
}

Anstatt für jede Schnittstelle eine Provider-Klasse zu schreiben und sie dann im Modul noch mal an die Schnittstelle zu binden, bietet Roboguice mit der Annotation @Provides eine Abkürzung. Man mit ihr eine öffentliche Methode in einem Modul markieren, die eine neue Instanz erzeugt. Der Rückgabe-Typ der Methode entspricht dem Typ, an den diese Provider-Methode gebunden wird.

public class BuildModule extends AbstractModule {
   // Diese Methode bindet den Typ „ICombustible“ 
   // an die Implementierung „Gaosline“
   @Provides
   public ICombustible getCombustible(){
      return new Gasoline();
   }
}

Named Injections

Manchmal kann es nötig sein, verschieden konfigurierte Instanzen des selben Typs injizieren zu können. Man braucht also eine Möglichkeit, einem einzelnen Typ mehre Provider zuzuordnen. Dazu müssen die entsprechenden Attribute, die eine Injektion erwarten zusätzlich mit der Annotation @Named() versehen werden, der ein String zur Unterscheidung übergeben wird. Mit den selben Annotationen werden dann in einem Modul die Provider-Methoden versehen, die die unterschiedlichen Instanzen des Typs produzieren.

@Inject
@Named("blue_team")
private Player player;
public class GameModule extends AbstractModule {
   @Provides
   @Named("blue_team")
   public Player getBluePlayer(){
      return new Player(new BlueSkin());
   }
   @Provides
   @Named("red_team")
   public Player getRedPlayer(){
      return new Player(new RedSkin());
   }
}

Statt mit Annotationen lassen sich Bindings mit Namen auch programmatisch ausdrücken durch die Methode annotatedWith(), die eine Annotation mit dem entsprechenden Namen entgegen nimmt. Ein solches Annotations-Objekt kann mit der Methode Names.named() erzeugt werden.

public class GameModule extends AbstractModule {
   @Override
   protected void configure() {
      this.bind(Player.class).annotatedWith(Names.named("blue_team"))
         .toProvider(BluePlayerProvider.class);
      this.bind(Player.class).annotatedWith(Names.named("red_team"))
         .toProvider(RedPlayerProvider.class);
   }
}
profile

Franziska Neumeister

Hat "Media Systems" studiert, entwickelt mobile Apps und will wissen, ob Androiden auch von elektrischen Schafen träumen

Navigation