Подтвердить что ты не робот

Как использовать класс AndroidInjection в пользовательских представлениях или других классах Android?

Моя проблема с шаблоном, зависящим от Android, заключается в том, что если вы используете свой класс AndroidInjection, члены-члены не могут вводить другие объекты, кроме Activities/Fragments/пользовательских представлений/адаптеров, за исключением компонента Application Component, Это связано с тем, что вы не можете получить ссылку Subcomponent (AndroidInjector), используемую для ввода Activities/Fragments. Это делает инъекционные диалоги (если вы используете DialogFragments).

Класс AndroidInjection, похоже, поддерживает только основные типы Android.

4b9b3361

Ответ 1

Далее следует не ответ на ваш вопрос, а объяснение, почему вы не должны задавать этот вопрос вообще.

Вам следует избегать инъекций в пользовательский Views в целом. Причины этого перечислены в в этой статье.

Преимущества использования метода Injection в этом случае [инъекция в пользовательские представления]:

  • Зависимости необходимо будет распространять из компонента верхнего уровня (Activity или Fragment)
  • Метод Injection не открывает дверь для нарушения принципа единой ответственности.
  • Никакая зависимость от структуры
  • Лучшая производительность

Первое преимущество может стать неожиданностью, поскольку распространение компонент верхнего уровня сложнее добавления аннотаций к полям и включает в себя более шаблонный код. Это, конечно, плохо, правда? Не в этом дело. На самом деле есть два хороших аспекта, связанных с такое распространение зависимостей. Прежде всего, зависимости будут видны на компоненте верхнего уровня. Поэтому, просто взглянув напр. Поля Фрагмента, читатель кода немедленно поймите, что этот фрагмент показывает изображения. Такая оптимизация для читаемость делает систему более удобной в обслуживании в длинных срок. Во-вторых, не так много случаев использования, когда подклассы Посмотрите на необходимость дополнительных зависимостей. Тот факт, что вам нужно на самом деле работа для обеспечения этих зависимостей даст вам немного время подумать о том, является ли предоставление им правильного дизайнерского решения для начала.

Второе преимущество связано с совместной конструкцией. Вы может быть очень опытным программистом, но youll вероятно, также менее опытные товарищи по команде. Или возможно, что однажды вы покинете проект, и тот, кто возьмет на себя не так хороши, как вы. Введя одну единственную зависимость, используя рамки, вы в основном открываете дверь для других инъекций. Представить что некоторые данные из SharedPreferences становятся обязательными в пользовательском представлении чтобы, например, исправить ошибку. Один из менее опытных разработчиков может решить, что это хороший подход для внедрения SharedPreferences в пользовательский вид напрямую. Это нарушает единую ответственность Принцип, но этот разработчик может даже не знать о таком концепция. Поэтому в долгосрочной перспективе такая инъекция "бэкдоров" может уменьшить качество дизайна и привести к длительным сеансам отладки.

Третье преимущество использования метода Injection с пользовательскими представлениями что вы не разделяете рамки вложений для зависимостей. Просто представьте себе, что через несколько лет вы (или какой-нибудь другой бедный парень) должны замените структуру. Тот факт, что вы, вероятно, десятки Действия и Фрагменты, чтобы начать, сделают вашу жизнь несчастной. Если у вас есть дополнительные десятки или сотни пользовательских представлений для обработки, то это может привести к самоубийственным настроениям.

Последнее (но не менее важное) преимущество - производительность. Один экран может содержат одно действие, несколько фрагментов и десятки пользовательских представлений. Загрузите это количество классов, используя инъекцию зависимостей может ухудшить производительность приложений. Это особенно истинно для основанных на размышлении оснований, но даже Кинжал несет некоторые производительность.

Кроме того, я советую избегать нового метода инъекции, который включает класс AndroidInjection. Это обсуждается в этом видеоуроке.

Ответ 2

Во-первых, вы должны подумать Василий ответить.

Но подумайте, как мы это делали раньше, чем Dagger Android? Мы построили подкомпонент из компонента, взятого из класса Application. Позже мы могли бы использовать этот подкомпонент для ввода полей, например, пользовательского представления.

Итак, мы постараемся сделать то же самое сейчас.

Предположим, наша цель - ввести класс MyAdapter в MyButton:

public class MyButton extends AppCompatButton {

    @Inject MyAdapter adapter;

    public MyButton(Context context) {
        super(context);

        ...
    }

}

И пусть у адаптера есть зависимость от действия Context, не Context:

public class MyAdapter {

    @Inject
    public MyAdapter(@Named("activity") Context context) {
    }

}

Давайте начнем с пользовательского класса Application.

MyApplication.java

public class MyApplication extends DaggerApplication {

    @Inject
    DispatchingAndroidInjector<Activity> dispatchingActivityInjector;

    public static MySubcomponent mySubcomponent;

    @Override
    protected AndroidInjector<? extends DaggerApplication> applicationInjector() {

        return DaggerAppComponent.builder()
                .create(this);
    }

}

AppComponent.java

@Component(modules = {AndroidSupportInjectionModule.class, ActivityBindingModule.class, AppModule.class})
@Singleton
public interface AppComponent extends AndroidInjector<MyApplication> {

    @Component.Builder
    abstract class Builder extends AndroidInjector.Builder<MyApplication> {
    }
}

AppModule.java

@Module
abstract class AppModule {

    @Binds
    @Singleton
    @Named("app")
    abstract Context providesContext(Application application);
}

ActivityBindingModule.java

@Module(subcomponents = MySubcomponent.class)
public abstract class ActivityBindingModule {

    @Binds
    @IntoMap
    @ActivityKey(MainActivity.class)
    abstract AndroidInjector.Factory<? extends Activity>
    bindMainActivityInjectorFactory(MySubcomponent.Builder builder);
}

AndroidSupportInjectionModule.java поставляется с самим кинжалом. Если вы не используете классы из пакета поддержки (т.е. android.app.Fragment вместо android.support.v4.app.Fragment), используйте AndroidInjectionModule.java.

MySubcomponent.java

@ActivityScope
@Subcomponent(modules = {SubcomponentModule.class/*, other modules here, if needed */})
public interface MySubcomponent extends AndroidInjector<MainActivity> {

    void inject(MyButton button);

    @Subcomponent.Builder
    abstract class Builder extends AndroidInjector.Builder<MainActivity> {
        public abstract MySubcomponent build();
    }
}

SubcomponentModule.java

@Module
abstract class SubcomponentModule {

    @Binds
    @ActivityScope
    @Named("activity")
    abstract Context toContext(MainActivity activity);

}

MainActivity.java

public class MainActivity extends AppCompatActivity {

    @Inject
    MySubcomponent subcomponent;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // Will inject `subcomponent` field
        AndroidInjection.inject(this);

        // Saving this component in a static field
        // Hereafter you are taking responsibility of mySubcomponent lifetime
        MyApplication.mySubcomponent = subcomponent;

        super.onCreate(savedInstanceState);
        setContentView(new MyButton(this));
    }
}

Имея все это, теперь вот как MyButton будет выглядеть так:

public class MyButton extends AppCompatButton {

    @Inject MyAdapter adapter;

    public MyButton(Context context) {
        super(context);

        MyApplication.mySubcomponent.inject(this);
    }

}

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

Ответ 3

Это связано с тем, что вы не можете получить ссылку Subcomponent (AndroidInjector), используемую для ввода Activities/Fragments.

Вы всегда можете просто вводить сам компонент. Просто добавьте поле для компонента в свой Activity/Fragment и дайте Кинжару ввести его вместе с остальными.

// will be injected
@Inject MainActivitySubcomponent component;

Ответ 4

Вопрос о том, должны ли классы кинжала-андроида, такие как AndroidInjector поддерживать инъекцию внутри представлений или нет, обсуждались в следующей проблеме Github:

https://github.com/google/dagger/issues/720

Цитата из одного из авторов библиотеки:

Здесь есть и философская точка, и точка логистики/реализации.

Во-первых, нам не совсем понятно, что инъекции представлений - это правильная вещь. Объекты просмотра предназначены для рисования, и не более того. Контроллер (в традиционном шаблоне MVC) - это тот, который может координировать и передавать соответствующие данные в представление. Внедрение представления размывает линии между фрагментами и представлениями (возможно, дочерний фрагмент действительно является подходящей конструкцией?)

С точки зрения реализации также существует проблема в том, что нет канонического способа извлечения родительских фрагментов View (если они есть) или Activity для извлечения родительского компонента. Были хаки, предлагающие строить в этих отношениях, но до сих пор мы не видели ничего, что могло бы предположить, что мы могли бы сделать это правильно. Мы могли бы просто вызвать View.getContext(). GetApplicationContext() и добавить оттуда, но пропустить промежуточные уровни без какой-либо опции для чего-то между ними не согласуются с остальной частью нашего дизайна и, вероятно, запутывают пользователей, даже если они работают.

Это подтверждает мнение, выраженное в ответе Василия.

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

Правильным решением для сопоставления представления и модели является написать адаптер, такой как адаптеры для RecyclerView и ListView. Вы можете ввести зависимость уровня модели на уровне фрагмента или презентатора и установить там адаптер.