I am currently trying to integrate Dagger 2 into an Android application. My project setup is as follows:
- library
- app (depends on library)
In my library project I defined a class that I'll later inject into other classes that need it (Activities and regular classes) in the library as well as the app project.
@Singleton
public class MyManager{
@Inject
public MyManager(){
//Do some initializing
}
}
Now - for instance in my Fragments or Activities or regular classes I'd inject the above Singleton as follows:
public class SomeClass{
@Inject
MyManager myManager;
}
Or so I thought, because in practice myManager is always null. And apparently it's constructor is never called either, so I guess I must be missing something configuration-wise? Or maybe I misunderstood the documentation and it's not meant to work this way at all? The purpose of MyManager class is to be an application-wide accessible component-accumulating entity - that's why I went for the @Singleton.
UPDATE
To avoid confusion: I mentioned my having components somewhere in a comment I think - this refers to components in the sense of "component based design" and has nothing to do with dagger. The dagger-based code I have is all listed above - there is nothing else related to dagger in my code.
When I started adding @Component I had some compiler issues, because my dagger2 was not setup properly - check out this really helpful thread on how to setup dagger2 correctly: https://stackoverflow.com/a/29943394/1041533
UPDATE 2
Here is my updated code, based on G. Lombard's suggestions - I changed the code as follows - the original Singleton is in the library project:
@Singleton
public class MyManager{
@Inject
public MyManager(){
//Do some initializing
}
}
Also in the library project is the bootstrap class:
@Singleton
@Component
public interface Bootstrap {
void initialize(Activity activity);
}
Then I use the above Bootstrap class in my activity (in my concrete app, NOT in the library project! I do however also have Classes/Activities in the library that'll access Bootstrap to inject MyManager):
public class MyActivity extends Activity{
@Inject
MyManager manager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//DONT DO THIS !!! AS EXPLAINED BY EpicPandaForce
DaggerBootstrap.create().initialize(this);
}
}
But even after this line:
DaggerBootstrap.create().initialize(this);
the manager instance is still null, i.e. not injected.
I just found this: https://stackoverflow.com/a/29326023/1041533
Which if I don't misread, implies I need to specify every single class in the Bootstrap class that will use @Inject to have stuff injected. Sadly - this is not an option, as I have more than 40 classes and activities for which I'd have to do that.
Meaning my Bootstrap interface apparently would have to look something like this:
@Singleton
@Component
public interface Bootstrap {
void initialize(ActivityA activity);
void initialize(ActivityB activity);
void initialize(ActivityC activity);
void initialize(ActivityD activity);
void initialize(ActivityE activity);
void initialize(ActivityF activity);
//and so on and so forth...
}
If the above is true, that would not be worth it for my use case. Plus: Seems there is no compile-time check, if I forgot to specify one of my 40+ classes here? It just wont work - i.e. crash the app at runtime.
You're making a mistake in that you are using
in your Activity, as scopes are not shared across multiple component instances. What I recommend is using a custom application class
Then you could call it as
This way, you share the component across your classes. I personally named
Bootstrap
asinjector
, and_Bootstrap
asApplicationComponent
, so it looks like this:But that's just my typical setup. Names don't really matter.
EDIT: To your last question, you can solve this by subscoping and component dependencies.
Your library project should be able to see only the library classes, correct? In that case, all you do is
Then
It's hard to say what your problem was, since you didn't show what your
Component
looks like and whether you have multiple components etc.Assuming this logical structure:
Then your classes as listed would inject correctly with the following configuration:
and the app-level component to inject dependencies into the activity:
Note that
MainComponent
depends onLibraryComponent
, but because the latter has singleton scope, you need to define a scope for the other one too, which I was I used the "activity scope" here. (Or you could also just make the MainComponent a singleton and get rid of the LibraryComponent completely if that suits your needs.)Finally it's all injected into the activity like this:
I've put a working sample here on GitHub
Update 1:
If I understand your setup correctly, you've so far only used the
@Singleton
and@Inject
annotations on the two classes listed (MyManager
andSomeClass
), and there is no other Dagger-related code in your project.In that case, the reason your
MyManager
isn't getting injected, is because Dagger doesn't know how to provide/instantiate the dependencies. This is where the "components" come in that I mentioned above. Without any Dagger 2 components (interface or abstract class annotated with@Component
), your dependencies won't get injected automatically.I don't know if you have experience with Dependency Injection concepts, but assuming you don't, I'll step through the minimum basics you'll need to understand to get your
MyManager
injected intoSomeClass
:First: when you use DI, you need to understand the difference between "newables" and "injectables". This blogpost by Misko Hevery explains the details.
This means, you can't
new
up yourSomeClass
. This won't work:Because if you did that (say in an activity or fragment), Dagger will have no idea that you expected a dependency to get injected into
SomeClass
and it has no opportunity to inject anything.In order for its dependencies to get injected, you have to instantiate (or inject)
SomeClass
itself through Dagger too.In other words, say in your Activity where
SomeClass
is used, you'll need:Next, you need a Dagger component to perform the actual injection. To create a component, you create an interface with a method that takes your root object (say
MainActivity
) as argument, e.g.:Now when you build your project, Dagger 2 generates a class called
DaggerBootstrap
that implements this interface. You use this generated class to perform the injection, say in your activity's onCreate:I believe this generated component is the key part you're missing. Full code for above here.
Some useful Dagger 2 resources:
Update 2:
Seems like the final missing piece of the puzzle was that your component provided an inject method for the
Activity
base class, but not for your actual concrete activity.Unfortunately Dagger 2 requires an inject method for each activity or other class you want to inject into.
As you mentioned, this will be annoying when you have many different activities in your app. There some possible workarounds for this, search for "dagger 2 inject base class", for example this suggestion by @EpicPandaForce: Dagger 2 base class injections
Also note, as pointed out by @EpicPandaForce in the comments, that in my simplistic example I called
DaggerLibraryComponent.create()
every time which is probably not what you want, since that component is supposed to provide your singletons, so you're probably better off getting the existing instance from somewhere else such as from your Application instance.