2015-04-03 14 views
12

Mit Dolch 2 versuche ich ein Singleton-Objekt an mehreren Orten in einem einzigen Bereich zu injizieren. Es scheint jedoch, dass meine Lösung stattdessen jedes Mal eine neue Instanz erstellt.Dolch 2 Singletons funktioniert nicht

In diesem Testprojekt habe ich eine MainActivity, die das DaggerModule initialisiert. Das DaggerModule stellt die Objekte Box und Cat zur Verfügung, wobei die Box Cat als Parameter verwendet. Ich nehme auch Cat in meiner MainActivity auf. Schließlich überprüfe ich die Referenzen der beiden injizierten Cat-Variablen (in der Box und in der MainActivity), aber sie sind nicht die gleiche Instanz.

Wenn ich provideCat() zweimal in meiner MainActivity stattdessen aufrufen, wird dieselbe Instanz bereitgestellt.

MainActivity:

public class MainActivity extends ActionBarActivity { 

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.activity_main); 
     DaggerModule daggerModule = new DaggerModule(); 
     DaggerComponent daggerComponent = Dagger_DaggerComponent.builder() 
       .daggerModule(daggerModule).build(); 

     // Same Cat instance returned. 
     Cat cat1 = daggerComponent.provideCat(); 
     Cat cat2 = daggerComponent.provideCat(); 
     Log.d("=== cat1: ", cat1.toString()); 
     Log.d("=== cat2: ", cat2.toString()); 

     // Different Cat instance returned. Why? 
     Box box = daggerComponent.provideBox(); 
     Log.d("=== box cat: ", box.getCat().toString()); 
    } 
} 

@Module 
public class DaggerModule { 

    @Provides 
    @Singleton 
    public Cat provideCat() { 
     return new Cat(); 
    } 

    @Provides 
    @Singleton 
    public Box provideBox() { 
     return new Box(provideCat()); 
    } 

} 

@Singleton 
@Component(modules = { DaggerModule.class }) 
public interface DaggerComponent { 

    Cat provideCat(); 

    Box provideBox(); 

} 

public class Cat { 

    @Inject 
    public Cat() { 
    } 

} 

public class Box { 

    private Cat mCat; 

    @Inject 
    public Box(Cat cat) { 
     mCat = cat; 
    } 

    public Cat getCat() { 
     return mCat; 
    } 

} 

Vielen Dank im Voraus!

Edit: Es funktioniert, wenn provideBox in einem Cat-Argumente und verwendet, dass die Box zu erstellen, statt Aufruf provideCat direkt aus provideBox.

// Doesn't work, new Cat instance created. 
    @Provides 
    @Singleton 
    public Box provideBox() { 
     return new Box(provideCat()); 
    } 

vs

// Works, same singleton injected. 
    @Provides 
    @Singleton 
    public Box provideBox(Cat cat) { 
     return new Box(cat); 
    } 

Was ist der Unterschied zwischen provideCat im MainActivity Aufruf und es von innen provideBox im DaggerModule tun? Könnte es sein, dass der Dagger-Compiler das DaggerModule nicht genauso wie bei externen Klassen verarbeitet und die Annotationen nicht angewendet werden, wenn ich provideCat dort anrufe?

Antwort

6

Der Grund, warum ich provideCat innerhalb von providerBox aufrufen wollte, war ein Missverständnis seitens der Komponentenschnittstelle. Ich habe missverstanden, dass das Component-Interface vom Modul nicht wirklich implementiert wurde, daher mussten die Argumente der Methoden des Moduls nicht in den entsprechenden Methoden der Komponente deklariert werden. Wäre das der Fall gewesen, hätte ich gezwungen, die Cat-Instanz im Aufruf der Methode "Methode" der MainActivity zu erstellen, die ich vermeiden wollte (und daher direkt in der Methode "provideCat" des Moduls auf "provideCat" zugreift). Das Deklarieren von Argumenten in den Component-Methoden führte sogar dazu, dass der Dagger-Compiler nicht kompilieren konnte.

Da die Component-Methoden jedoch keine Argumente verwenden, bestand die Lösung einfach darin, Instanzen als Argumente in die Module-Methoden einzufügen (statt die entsprechenden Bereitstellungsmethoden innerhalb des Moduls selbst aufzurufen) und nur aufzurufen die parameter Component Methoden aus dem MainActivity wie folgt:

MainActivity:

Cat cat = daggerComponent.provideCat(); 
Box box = daggerComponent.provideBox(); 

Komponente:

Cat provideCat(); 
Box provideBox(); <- no arguments 

Modul:

@Module 
public class DaggerModule { 

    @Provides 
    @Singleton 
    public Cat provideCat() { 
     return new Cat(); 
    } 

    @Provides 
    @Singleton 
    public Box provideBox(Cat cat) { <- arguments 
     return new Box(cat); 
    } 

} 

Die Katze Singleton Instanzen der MainActivity und Box sind nun gleich und ich habe nicht sie von der MainActivity zu erklären, aber Dagger behandelt sie alle. Erfolg! Ich bin mir immer noch nicht sicher, warum Methoden anders funktionieren, wenn sie von externen Klassen aufgerufen werden als von Modulen selbst.

+2

Der Grund dafür ist, dass das Instanzenmanagement von der Komponentenimplementierung und nicht vom Modul gehandhabt wird. Wenn Sie die Methode direkt aufrufen, wird sie nur wie jede andere Java-Methode aufgerufen, aber wenn Sie die Komponenten die Abhängigkeiten miteinander verbinden lassen, wird ein 'dagger.internal.ScopedProvider' verwendet, um sicherzustellen, dass es nur eine Instanz gibt. – gk5885

+5

Auch für Typen, die '@ Inject'ed sind, können Sie einfach die Klasse (in diesem Fall' Cat' und 'Box') mit' @ Singleton' direkt annotieren und die '@ Providents'-Methoden vermeiden. – gk5885

+0

Danke für die Info und Tipp! – Johe