I'm new to Dagger and at the begininig I face some issues. I have simple structure so far in my project. My injection module:
@Module(
injects = {GameBoardFragment.class, GameManager.class},
complete = false,
library = true
)
public class GameObjectsProviderModule {
private final Application mApplication;
public GameObjectsProviderModule(Application application){
this.mApplication = application;
}
@Provides
@Singleton
public GameManager provideGameManager(){
return new GameManager();
}
@Provides
public Board getBoard(){
return new Board();
}
@Provides @Singleton @ForApplication Context provideAppContext() {
return mApplication;
}
My simplified custom app class looks like that:
public class MyApp extends Application {
private static ObjectGraph mApplicationGraph;
@Override public void onCreate() {
super.onCreate();
mApplicationGraph = ObjectGraph.create(new GameObjectsProviderModule(this));
}
public static ObjectGraph getObjectGraph(){
return mApplicationGraph;
}
}
And now, my fragment looks like that:
public class GameBoardFragment extends Fragment {
@Inject
GameManager mGameManager;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
MyApp.getObjectGraph().inject(this);
View root = inflater.inflate(R.layout.fragment_game_board, container, false);
findViews(root);
confViews();
return root;
}
}
And finally my GameManager class
public class GameManager {
@Inject Board mBoard;
public GameManager(){
MyApp.getObjectGraph().inject(this);
}
}
Andy hey, it works! Great. But my question is why it doesn't work in case I comment out this line:
MyApp.getObjectGraph().inject(this);
Do we have always explicitly call inject() function to make all necessary injections take place event in nested objects? It looks not, as shows coffe maker example:
https://github.com/square/dagger/tree/master/examples/simple/src/main/java/coffee
Why then I have to call inject() in GameManager class to get it working?
Edit:
The consturctor injection approach works just fine.
But for future use I tried to get field injection running, and so far I haven't succeed.
I commented out both @Provide methods from module and I made my GameManager look like this:
@Singleton
public class GameManager {
@Inject Board mBoard;
@Inject
public GameManager(){
}
}
and Board:
public class Board {
@Inject
public Board() {
}
}
However mBoard doesn't get instantiated. I will try more and I suppose I figure out the proper solution.
You should rather use constructor injection (like for example the Thermosiphon does), and avoid field injection unless necessary. For example, let your GameManager
have the Board
as a constructor argument:
@Singleton
public class GameManager {
private final Board mBoard;
@Inject
public GameManager(final Board board){
mBoard = board;
}
}
Dagger will use this constructor to create an instance of the GameManager
(hence the @Inject
annotation), and notice it needs a Board
instance. Using the ObjectGraph
, it will create a Board
first, and use that instance to create the GameManager
. You can remove the @Provides GameManager
method if you do it this way.
In your case, you have a @Provides Board
method in your module. If you add an @Inject
annotation to your Board
constructor, you can remove this provides-method from your module:
public class Board {
@Inject
public Board() {
}
}
If you don't want to use constructor injection, the problem is that you told Dagger that you want to create your GameManager
instance yourself (because you have the @Provides GameManager
method). If you remove this method, and let Dagger create it for you like above but without the Board
parameter in the constructor, Dagger will also notice the @Inject Board
field and inject that as well.
A final remark. Remove the library = true
and complete = false
statements! These are not necessary at all in this example. Only add them if you really know what you're doing. By not having them, Dagger will create compile-time errors to notify you that something is wrong. If you do include them, you're telling Dagger "Hey, I know what I'm doing, don't worry, it's all correct", when in fact it isn't.
Edit
A quote from the Dagger1 site:
If your class has @Inject-annotated fields but no @Inject-annotated constructor, Dagger will use a no-argument constructor if it exists. Classes that lack @Inject annotations cannot be constructed by Dagger.
I do not use this method very often, so I could be wrong. I think this means that you should remove the @Inject
annotation from your constructor, like so:
@Singleton
public class GameManager {
@Inject Board mBoard;
public GameManager(){ // Or remove the constructor entirely since it's empty
}
}
Since there is an @Inject
annotation on the Board
field, Dagger will know to use the no-argument constructor.
I was struggling with the same issue as most of the dagger examples everywhere use a Module with Provides and I had a hard time finding a complete example that just does not use Provides.
I created this one. It uses field injection (not constructor injection) and works just fine through the hierarchy without requiring any call to inject. I am using Dagger 1.2.2.
Main.java
import javax.inject.*;
import dagger.*;
import dagger.ObjectGraph;
public class Main {
public static void main(String[] args) {
ObjectGraph objectGraph = ObjectGraph.create(new CarModule());
Car car = objectGraph.get(Car.class);
car.start();
}
}
CarModule.Java
import dagger.Module;
@Module(injects = Car.class)
public class CarModule {
}
Car.Java
import javax.inject.*;
public class Car {
@Inject public Engine engine;
@Inject Car() {
System.out.println("Car constructor");
}
public void start() {
engine.start();
}
}
Engine.Java
import javax.inject.*;
public class Engine {
@Inject WaterPump waterPump;
Engine() {
System.out.println("Engine Constructor");
}
void start() {
waterPump.run();
System.out.println("starting engine.");
}
}
WaterPump.Java
import javax.inject.*;
public class WaterPump {
@Inject WaterPump() {
System.out.println("WaterPump Constructor.");
}
public void run() {
System.out.println("WaterPump running.");
}
}
The output is:
Car constructor
Engine Constructor
WaterPump Constructor.
WaterPump running.
starting engine.
Without The CarModule
that declares it injects Car.Class this does not work. You get:
Exception in thread "main" java.lang.IllegalArgumentException: No inject registered for members/Car. You must explicitly add it to the 'injects' option in one of your modules.
But notice that CarModule
does not @Provides
anything. It is dagger that automatically creates all the dependencies using the object graph.
Also note that you don't have to place an @Inject
annotation on the default constructor if you have a @Inject
field in the class. For Car
class I used it on both the constructor and the field and in Engine
class I used it just for the field and not for the constructor and it works fine as documented.
来源:https://stackoverflow.com/questions/28330117/dagger-nested-injections-is-it-necessary-to-call-inject