简体   繁体   中英

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

  • 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?

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 .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM