Почему мое поле "пусто" после инъекции? Как я могу ввести свой объект? - PullRequest
0 голосов
/ 10 мая 2018

Это канонический вопрос , поскольку существует множество заблуждений относительно инициализации объекта с помощью Dagger 2.

Если ваш вопрос был помечен как дубликат , пожалуйста, внимательно прочитайте этот пост и убедитесь, что вы понимаете разницу между инжектором конструктора и инжекцией поля.

Я пытаюсь внедрить Context в свой докладчик, но я получаю исключение NullPointerException при попытке его использовать.

class MyPresenter {

  @Inject Context context;

  private MyView view;

  @Inject
  MyPresenter(MyView view) {
    this.view = view;
  }
}

Мой модуль выглядит так

@Module
class MyModule {

  @Provides
  MyPresenter provideMyPresenter(MyView view) {
    return new MyPresenter(view);
  }
}

Я ввожу докладчика в свою активность здесь:

class MyActivity extends Activity {

  @Inject MyPresenter presenter;

  @Override
  public void onCreate(Bundle savedInstanceState) {
    createMyActivityComponent().inject(this);
  }
}

Ответы [ 2 ]

0 голосов
/ 10 мая 2018

Удалите @ Inject из Context и создайте отдельный модуль для предоставления Context зависимость

@Module 
public class ContextModule {
    private final Context context;

    public ContextModule(Context context) {
        this.context = context;
    }

    @Provides
    @MyAppScope
    public Context getContext() {
        return context;
    }
}

затем создайте свой DaggerComponent. (Я создал его в классе приложения, и это относится к ApplicationContext

component =  DaggerDaggerAppComponent.builder()
            .contextModule(new ContextModule(this))
            .MyModule()
            .build();

Вы можете пропустить .MyModule () , если хотите, потому что в отличие от Контекстный модуль он не имеет внешней зависимости.

0 голосов
/ 10 мая 2018

Вышесказанное включает в себя как конструктор, так и инжекцию поля , но не выполнено правильно .Пример будет вести себя так же, если мы удалим все аннотации @Inject из MyPresenter, так как мы не используем ни одну из них.

@Provides
MyPresenter provideMyPresenter(MyView view) {
  // no constructor injection, we create the object ourselves!
  return new MyPresenter(view);
}

// also no mention anywhere of component.inject(presenter)
// so the fields won't be injected either

Убедитесь, что вы используете либо конструкторинъекция или полевая инъекция.Смешение обоих обычно указывает на ошибку в вашей настройке или понимании.

  • @Inject в поле является маркером для ввода поля
  • @Inject вконструктор является маркером для инжекции конструктора

Это означает, что ваш класс должен иметь либо

  • a одиночные @Inject в конструкторе или
  • a @Inject в все поля для инициализации , но нет в конструкторе !

Не разбрызгивайте @Inject везде и ожидайте, что все будет работать! Обязательно размещайте аннотацию там, где это необходимо.Не смешивайте инъекцию поля и конструктора!

Инжектор конструктора должен быть более предпочтительным, чем инъекция поля , поскольку он создает инициализированный и пригодный для использования объект.Инжекция поля должна использоваться с компонентами Framework, где Framework создает объекты.Вы должны вручную вызвать component.inject(object) для выполнения вставки поля, или любые аннотированные поля будут иметь значение при попытке их использования.

Внедрение в конструктор

Какимя предлагает вам поместить ваши зависимости в качестве параметров в конструкторе .Аннотация к конструктору сообщает Dagger об объекте, и он может создать объект для вас , вызвав его со всеми необходимыми зависимостями.Кинжал также будет внедрять любые аннотированные поля или методы после создания объекта, но внедрение простого конструктора обычно следует поощрять, поскольку оно не скрывает никаких зависимостей.

Создание объекта кинжалом также означает, что не нужнодля @Provides метода в вашем модуле , который создает объект.Все, что вам нужно сделать, это добавить @Inject в конструктор и объявить зависимости.

class MyPresenter {

  private Context context;
  private MyView view;

  @Inject
  MyPresenter(MyView view, Context context) {
    this.view = view;
    this.context = context
  }
}

Если вы хотите связать свою реализацию с интерфейсом, вам все равно не нужно создавать объект самостоятельно.

@Module class MyModule {

  @Provides
  MyPresenter providePresenter(MyPresenterImpl presenter) {
    // Dagger creates the object, we return it as a binding for the interface!
    return presenter;
  }
}

И есть даже более короткая (и более производительная) версия приведенного выше варианта использования:

@Module interface MyModule {

  @Binds
  MyPresenter providePresenter(MyPresenterImpl presenter)
}

Внедрение в конструктор должно быть вашим стандартным способом использования Dagger.Убедитесь, что вы не звоните new сами или неправильно поняли концепцию.

Инъекция поля

Бывают случаи, когда вы не можете используйте инъекцию конструктора, например, Activity в Android создается Framework, и вы не должны переопределять конструктор.В этом случае мы можем использовать инъекцию поля .

. Чтобы использовать инъекцию поля, аннотируйте все поля, которые вы хотите инициализировать, с помощью @Inject и добавьте метод void inject(MyActivity activity) к компоненту, который долженобработайте инъекцию.

@Component
interface MyComponent {
  void inject(MyActivity activity);
}

И где-то в вашем коде вам нужно позвонить component.inject(myActivity) , иначе поля не будут инициализированы. например, в onCreate(..)

void onCreate(..) {
  // fields still null / uninitialized
  myComponent.inject(this);
  // fields are now injected!

  // ...
}

Инжекция поля не транзитивна .Тот факт, что вы вводите действие, не означает, что Dagger также будет вводить поля выступающего, которого он ввел.Вы должны вводить каждый объект вручную, что является одной из причин, почему вы должны отдавать предпочтение инжектору конструктора.

Существуют инструменты, которые помогают смягчить шаблон создания компонентов и внедрения ваших объектов, например AndroidInjection.inject(), которые сделают это за вас., но это еще предстоит сделать.Другой пример - AppInjector, который добавляет различные прослушиватели жизненного цикла для внедрения ваших действий и фрагментов, но все равно будет вызываться AndroidInjection, который затем создает ваш компонент и внедряет объект.

Убедитесь, что вы внедрили объект перед его использованием и что нет конструктора, помеченного @Inject, чтобы избежать путаницы.

Что еще?

Существует также менее используемый метод внедрения и, конечно, Dagger не может внедрять сторонние библиотеки, которые вы должны создавать и предоставлять в своих модулях.

...