Reputation: 28417
I have 2 modules: AndroidModule
for providing Context
-related objects and PhotosModule
providing PhotoManager
.
I want to use PhotoManager
in PhotosFragment
. PhotoManager
depends on DbManager
, which is provided by AndroidModule
.
How do I connect all the dots?
The code compiles, but a NPE is thrown on this line:
mDbManager.readDatabase();
meaning that the injection of DbManager
into PhotoManager
did not occur, even though I set the
injects = PhotoManager.class
in AndroidModule
.
Also, mDbManager
is not a private field, so I don't think I have to use injection on PhotoManager
's constructor; field injection should be sufficient.
What's the reason and how do I satisfy the missing dependency? I assume there may be more than one problem with my code. Note: I just started with Dagger, please keep that in mind.
Application:
public class TestApp extends Application {
private ObjectGraph mObjectGraph;
@Override
public void onCreate() {
super.onCreate();
mObjectGraph = ObjectGraph.create(getModules().toArray());
}
protected List<Object> getModules() {
return Arrays.asList(new AndroidModule(this), new PhotosModule());
}
public void inject(Object object) {
mObjectGraph.inject(object);
}
}
AndroidModule:
@Module(library = true, injects = PhotoManager.class)
public class AndroidModule {
private final TestApp mApplication;
public AndroidModule(TestApp application) {
mApplication = application;
}
@Provides
@Singleton
@ForApplication
Context provideApplicationContext() {
return mApplication;
}
@Provides
@Singleton
DbManager provideDbManager() {
return new DbManager(mApplication);
}
}
PhotosModule:
@Module(injects = PhotosFragment.class)
public class PhotosModule {
@Provides
@Singleton
PhotoManager providePhotoManager() {
return new PhotoManager();
}
}
PhotoManager:
@Singleton
public class PhotoManager {
@Inject
DbManager mDbManager;
public void doSomething() {
mDbManager.readDatabase();
}
}
PhotosFragment:
public class PhotosFragment extends Fragment {
@Inject
PhotoManager mPhotoManager;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
((TestApp) getActivity().getApplication()).inject(this);
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
///...
mPhotoManager.doSomething();
}
}
DbManager:
@Singleton
public class DbManager {
public DbManager(Context context) {
//...
}
public void readDatabase() {
//...
}
}
Upvotes: 0
Views: 844
Reputation: 3679
Here's the core of the problem: in PhotosModule
, you have the provider method:
PhotoManager providePhotoManager() {
return new PhotoManager();
}
You are new
ing up the PhotoManager yourself, so Dagger doesn't have an opportunity to inject its dependency (the DbManager
).
I think it's because you're misunderstanding the meaning of the injects
property on the @Module(injects=...)
annotation of the AndroidModule
. You have:
@Module(library = true, injects = PhotoManager.class)
But that injects
isn't needed, because you're never calling objectGraph.inject on the PhotoManager. Instead, you're only injecting the PhotoFragment
, which is correct...
So one way to fix the NPE problem:
remove the injects=PhotoManager.class
from AndroidModule.java
:
@Module(library = true)
change PhotosModule
to include AndroidModule
:
@Module(injects = PhotosFragment.class, includes = AndroidModule.class)
I avoid field injection using the @Inject
annotation (only use it for the top-level objects, i.e. where you do the objectgraph.inject, such as activity and fragment level), and use constructor injection instead for all the other dependencies. So I'd change PhotoManager
to inject the DbManager
like this:
private final DbManager mDbManager;
public PhotoManager(DbManager dbManager) {
mDbManager = dbManager;
}
I've put up the code here on GitHub.
Upvotes: 2