问题
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.
回答1:
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.
回答2:
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