Reputation: 803
Unfortunately I fail to use even the simplest Dagger examples. Let's take a look at my code (I got rid most of UI and auto-generated code as it was not relevant):
My Activity
public class MainActivity extends ActionBarActivity {
@Inject
RoomDAO roomDAO;
private ObjectGraph objectGraph;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
objectGraph = ObjectGraph.create(RoomModule.class);
roomDAO = objectGraph.get(RoomDAO.class);
roomDAO.hai();
}
}
RoomDAO & RoomDAOImpl
public interface RoomDAO {
String hai();
}
public class RoomDAOImpl implements RoomDAO {
private DBHelper dbHelper;
@Inject
public RoomDAOImpl(){
//I tried even this constructor in case dbHelper was the guy causing problems,
//but it wasn't!
}
public RoomDAOImpl(DBHelper dbHelper){
this.dbHelper = dbHelper;
}
@Override
public String hai() {
return "oh hai";
}
}
RoomModule
@Module(
injects = {MainActivity.class},
library = true
)
public class RoomModule {
@Provides @Singleton
public RoomDAO provideRoomDao(){
return new RoomDAOImpl();
}
}
DBHelper
doesn't have anything interesting (just some little database stuff, onCreate, onUpdate, nothing more really).
I thought that line roomDAO = objectGraph.get(RoomDAO.class);
would make Dagger to (more or less) call RoomModule#provideRoomDao()
and make me a nice, brand new RoomDAOImpl object (or take it from the sky if it is already created). However, my graph does not have RoomDAO
in injectableTypes
, so my app is failing at line roomDAO = objectGraph.get(RoomDAO.class);
when calling ObjectGraph#getInjectableTypeBinding
(because moduleClass == null
). What am I missing?
EDIT.
OK, probably this is the time to show how DBHelper looks like.
public class DBHelper extends SQLiteOpenHelper {
// (...) not relevant config stuff i.e. DATABASE_NAME, DATABASE_VERSION
@Inject
public DBHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
//(...) creating tables
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
//(...) updating tables
}
}
@Module(
injects = {MainActivity.class}
)
public class RoomModule {
@Provides @Singleton
public RoomDAO provideRoomDao(DBHelper dbHelper){
return new RoomDAOImpl(dbHelper);
}
@Provides
public Context getAppContext(){
return MainActivity.getAppContext();
}
}
I decided to use second approach you suggested, as it seems to be more Spring-like and I am more familiar with that. MainActivity.getAppContext();
is a static method that returns the value of static field Context. It's a little embarrassing, but I'm pretty ignorant if it comes to Context as I have never seen the real usage of it (I was always putting this
where I needed Context).
That's where comes the question - does RoomModule#getAppContext()
seem to be a probable source of bugs? DBHelper
probably will be fine, because it is a singleton and once created it should be theoretically available everywhere. Is adding some qualifier to this guy @Provides public Context getAppContext()
going to make this solution more or less free of bugs? Or maybe there is a better way to provide a Context for DBHelper
constructor?
Upvotes: 1
Views: 549
Reputation: 100398
First of all, remove the library = true
line. Do not just add library = true
or complete = false
, especially in basic examples. These lines surpress helpful warnings.
When running the application, you will get an IllegalArgumentException
:
Caused by: java.lang.IllegalArgumentException: No inject registered for tmp.RoomDAO. You must explicitly add it to the 'injects' option in one of your modules.
There are two ways to retrieve instances from the ObjectGraph
, using get(...)
, or using inject(...)
.
When using get(...)
, you will need to add the class you try to get to the injects
option in your module (like the exception tells you to):
@Module(injects = {MainActivity.class, RoomDAO.class})
This will make your application work. You actually don't need the @Inject
annotation on your RoomDAO
field.
Another way is to use inject
, with wich you do need the @Inject
annotation. inject
looks at the annotated fields of the class, and uses the ObjectGraph
to assign values to them. Using this method, you don't need to add RoomDAO
to the injects
option of your module:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
objectGraph = ObjectGraph.create(RoomModule.class);
objectGraph.inject(this);
roomDAO.hai();
}
Finally, your RoomDAOImpl
depends on DBHelper
. For completeness, you can do that like this:
DBHelper:
public class DBHelper {
@Inject
public DBHelper() {
}
}
RoomModule:
@Module(
injects = {MainActivity.class}
)
public class RoomModule {
@Provides
@Singleton
public RoomDAO provideRoomDao(DBHelper dbHelper) {
return new RoomDAOImpl(dbHelper);
}
}
RoomDAOImpl:
public class RoomDAOImpl implements RoomDAO {
private final DBHelper mDBHelper;
public RoomDAOImpl(final DBHelper dbHelper) {
mDBHelper = dbHelper;
}
@Override
public String hai() {
return "oh hai";
}
}
The DBHelper
instances in provideRoomDao
is created for you by Dagger. To do that, Dagger needs an @Inject
annotation on the constructor of DBHelper
. Since you instantiate RoomDAOImpl
yourself, you don't need to add the @Inject
annotation to the constructor of RoomDAOImpl
.
Upvotes: 1