Dagger 2 и реализации интерфейса

У меня есть простая тестовая установка Dagger 2, основанная на http://konmik.github.io/snorkeling-with-dagger-2.html. Он внедряет PreferenceLogger, который выводит все настройки. В введенном классе я могу @Inject больше классов.

public class MainActivity extends Activity {
    @Inject PreferencesLogger logger;
    @Inject MainPresenter presenter;

    @Override protected void onCreate(Bundle savedInstanceState) {
    MyApplication.getComponent().inject(this);
    presenter.doStuff();
        logger.log(this);
    }
}


public class PreferencesLogger {

    @Inject OkHttpClient client;
    @Inject public PreferencesLogger() {}

    public void log(Contect context) {
    // this.client is available
    }
}

Когда я запускаю это, устанавливается регистратор, а внутри PreferencesLogger.log правильно устанавливается OkHttpClient. Итак, этот пример работает так, как ожидалось. Теперь я пытаюсь создать структуру MVP. Есть интерфейс MainPresenter с реализацией. В MainActivity я установил:

@Inject MainPresenter presenter;

поэтому я мог бы переключить этот MainPresenter на альтернативную (отладочную или тестовую) реализацию. Конечно, теперь мне нужен модуль, чтобы указать, какую реализацию я хочу использовать.

public interface MainPresenter {
    void doStuff();
}

public class MainPresenterImpl implements MainPresenter {

    @Inject OkHttpClient client;

    public MainPresenterImpl() {}

    @Override public void doStuff() {
    // this.client is not available    
    }
}


@Module public class MainActivityModule {
    @Provides MainPresenter provideMainPresenter() {
        return new MainPresenterImpl();
    }
}

Теперь возникает проблема, заключающаяся в том, что OkHttpClient больше не внедряется. Конечно, я мог бы изменить модуль, чтобы он принимал параметр OkHttpClient, но я не думаю, что это рекомендуемый способ сделать это. Есть ли причина, по которой MainPresenterImpl неправильно внедряет?


person R. Adang    schedule 23.04.2015    source источник
comment
Я задал связанный вопрос здесь: stackoverflow.com /вопросы/30555285/   -  person EpicPandaForce    schedule 31.05.2015
comment
взгляните на эту статью и пример проекта, который может помочь: medium.com/@m_mirhoseini/   -  person Mohsen Mirhoseini    schedule 30.11.2016


Ответы (2)


В отличие от внедрения конструктора, @Inject аннотированные поля зависимостей, созданные в @Provides методах, не могут быть внедрены автоматически. Для возможности внедрения полей требуется компонент, предоставляющий тип поля в своих модулях, а в самих методах провайдера такая реализация недоступна.

Когда поле presenter вводится в MainActivity, все, что происходит, это вызывается метод поставщика, и presenter устанавливается в его возвращаемое значение. В вашем примере конструктор без аргументов не выполняет инициализацию, как и метод поставщика, поэтому инициализация не происходит.

Однако метод провайдера имеет доступ к экземплярам других типов, предоставленных в модуле через его параметры. Я думаю, что использование параметров в методе провайдера на самом деле является предлагаемым (даже единственным) способом «внедрить» зависимости предоставленного типа, потому что он явно указывает их как зависимости внутри модуля, что позволяет Dagger выдать ошибку при компиляции -время, если они не могут быть удовлетворены.

Причина, по которой в настоящее время не выдается ошибка, заключается в том, что MainPresenterImpl может удовлетворить свою OkHttpClient зависимость, если MainPresenterImpl, а не MainPresenter где-то является целью для внедрения. Dagger не может создать метод внедрения членов для типа интерфейса, потому что как интерфейс он не может иметь вводимые поля и не будет автоматически вводить поля реализующего типа, потому что он просто предоставляет любой метод поставщика. возвращается.

person moskvax    schedule 06.05.2015

Вы можете внедрить свой MainPresenterImpl с помощью внедрения конструктора.

/* unscoped */
public class MainPresenterImpl implements MainPresenter {

    @Inject 
    OkHttpClient client;

    @Inject
    public MainPresenterImpl() {
    }

    @Override public void doStuff() {
       // this.client is now available! :)
    }
}


@Module 
public class AppModule {
    private MyApplication application;

    public AppModule(MyApplication application) {
        this.application = application;
    }

    @Provides
    /* unscoped */ 
    public MyApplication application() {
        return application;
    }
}

@Module 
public abstract class MainActivityModule {
    @Binds public abstract MainPresenter mainPresenter(MainPresenterImpl mainPresenterImpl);
}
person EpicPandaForce    schedule 31.05.2015