spoko
spoko

Reputation: 803

Dagger constantly refuses to inject dependency

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):

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

Answers (1)

nhaarman
nhaarman

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

Related Questions