2014-11-30 3 views
1

К сожалению, я не использую даже самые простые примеры кинжалов. Давайте посмотрим на мой код (я избавилась большинство UI и автоматически генерируемой кода, как это было не актуально):
Кинжал постоянно отказывается вводить зависимость

  • Моя активность

    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 не имеет ничего интересного (только некоторые маленькие вещи баз данных, OnCreate, OnUpdate, больше ничего на самом деле).

Я думал, что линия roomDAO = objectGraph.get(RoomDAO.class); заставит Кинжал (более или менее) позвонить RoomModule#provideRoomDao() и сделать меня приятным, новым объектом RoomDAOImpl (или взять его с неба, если он уже создан). Тем не менее, мой график не имеет RoomDAO в injectableTypes, поэтому мое приложение не работает по строке roomDAO = objectGraph.get(RoomDAO.class); при вызове ObjectGraph#getInjectableTypeBinding (потому что moduleClass == null). Что мне не хватает?

EDIT.
ОК, возможно, настало время показать, как выглядит DBHelper.

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(); 
    } 
} 


я решил использовать второй подход вы предложили, как это кажется более весна, как и я больше знаком с этим.
MainActivity.getAppContext(); - статический метод, возвращающий значение статического поля Context. Это немного смущает, но я довольно неосведомлен, если дело доходит до контекста, поскольку я никогда не видел его реального использования (я всегда ставил this, где мне нужен контекст).
Вот где возникает вопрос - RoomModule#getAppContext() кажется вероятным источником ошибок? DBHelper, вероятно, будет хорошо, потому что это синглтон и когда-то созданный он должен быть теоретически доступен повсюду. Добавляет ли какой-то определитель этому парню @Provides public Context getAppContext() собирается сделать это решение более или менее свободным от ошибок? Или, может быть, есть лучший способ предоставить контекст для конструктора DBHelper?

ответ

1

Прежде всего, удалите линию library = true. Не просто добавьте library = true или complete = false, особенно в основных примерах. Эти строки подавляют полезные предупреждения.

При запуске приложения, вы получаете 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. 

Есть два способа восстановить экземпляры из ObjectGraph, используя get(...) или с использованием inject(...).

  • При использовании get(...), вам нужно будет добавить класс, который вы пытаетесь добраться до опции injects в модуле (как исключение говорит вам):

    @Module(injects = {MainActivity.class, RoomDAO.class}) 
    

    Это сделает ваше приложение Работа. Вам действительно не нужна аннотация @Inject на поле RoomDAO.

  • Другой способ заключается в использовании inject, с которым вы сделать нужна @Inject аннотацию. inject рассматривает аннотированные поля класса и использует ObjectGraph для присвоения им значений. Используя этот метод, вам не нужно добавлять RoomDAO к injects выбору модуля:

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
        super.onCreate(savedInstanceState); 
    
        objectGraph = ObjectGraph.create(RoomModule.class); 
        objectGraph.inject(this); 
        roomDAO.hai(); 
    } 
    

Наконец, ваш RoomDAOImpl зависит от DBHelper. Для полноты, вы можете сделать это так:

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"; 
    } 
} 

В DBHelper экземпляры в provideRoomDao создан для Вас Кинжал. Для этого Кинжал нуждается в аннотации @Inject на конструкторе DBHelper. Поскольку вы создаете экземпляр RoomDAOImpl самостоятельно, вам не нужно добавлять аннотацию @Inject к конструктору RoomDAOImpl.

+0

Здравствуйте, спасибо за ваше время и ответ, который сработал. Я решил использовать второй подход, который вы предложили. Во всяком случае, вы предсказывали, что я буду делать дальше, и здесь идет не-макетная версия конструктора RoomDAO. Я отредактировал свой вопрос, поскольку у DBHelper нет этого тривиального конструктора, поскольку он использует Context. Не могли бы вы еще раз взглянуть на мой вопрос и попытаться оценить, совершил ли я преступление или нет? ;) – spoko

+0

Да, это правильный настрой. Лучший способ предоставить контекст - передать его конструктору вашего RoomModule и сохранить его как поле. Тогда вам не нужен статический вызов getAppContext. – nhaarman

+0

Обратите внимание, что вы сделали правильную вещь, чтобы вернуть контекст приложения, а не только экземпляр Activity. Обход контекста активности опасен и известен как источник утечки памяти. – nhaarman

Смежные вопросы