0

У меня есть MainActivity, для которого требуется MeasurementFragment, который, в свою очередь, требует конвертера.Внедрение инъецируемого фрагмента (без дублирующих компонентов)

Проблема заключается в том, что при введении MesurementFragment на [1], его собственная зависимость converter[2] остается неинъекционной.

class MainActivity extends AppCompatActivity { 
    @Inject MeasurementFragment measureFrag; 

    @Override 
    protected void OnCreate(Bundle b){ 
     //[1]* 
     DaggerMainActivityComponent().create().inject(this); 
    } 
} 

Есть ли способ иметь инъекции «каскад», так что converter вводится при measureFrag впрыскивается?

Я мог бы создать второй экземпляр компонента DaggerMainActivityComponent в конструкторе ([3]) и ввести туда, но это звучит как плохая идея.

class MeasurementFragment extends Fragment { 

    // [2]* 
    @Inject Converter converter; 

    // required *empty* constructor 
    MeasrementFragment(){ 
     // [3]* 
    } 
} 

Другой потенциально соответствующий код (компонент и модуль):

@Component 
interface MainActivityComponent(){ 
    void inject(MainActivity ma); 
    void inject(MeasurementFragment mf); // << not used :(
} 

@Module 
class MainActivityModule{ 
    @Provides MeasurementFragment getMF(){ return new MeasurementFragment(); } 
    @Provides Converter getConverter(){ return new Converter(); } 
} 

Есть ли лучший дизайн для достижения этой цели?

+0

Вы должны ввести MeasurementFragment в график зависимостей в [3]. Я думаю, что это единственный способ. Для части дизайна вы можете создать объект DaggerMainActivityComponent в MainActivity, затем получить доступ и вставить его в фрагмент, используя getter и setter. –

+0

Попробуйте добавить конструктор по умолчанию для MeasurementFragment, аннотированный с помощью Inject. Удалите введенный void (MeasurementFragment mf); от вашего компонента и ваш метод Предоставление метода MeasurementFragment из вашего модуля. – ootinii

ответ

1

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

В случае фрагментов best practice для создания экземпляров должен использовать статические методы фабрики. Несмотря на то, что предыдущая структура DI, Roboguice, поощряла инъекцию фрагментов в качестве свойств Activity (@Inject MeasurementFragment mf), с Dagger 2, может быть, лучше попробовать другой подход. Причиной этого является то, что нам необходимо согласовать с FragmentManager для деятельности:

Для данного действия Фрагменты обрабатываются . Когда вызывается Activity onSaveInstanceState(Bundle outBundle) (например, в условиях низкой памяти), FragmentManager сохранит состояние экземпляра каждого фрагмента для восстановления внутри onCreate(Bundle savedInstanceState);.

Вот почему вы часто видите в Android примеры:

if (savedInstanceState == null) { 
     // During initial setup, plug in the details fragment. 
     DetailsFragment details = new DetailsFragment(); 
     details.setArguments(getIntent().getExtras()); 
     getFragmentManager().beginTransaction().add(android.R.id.content, details).commit(); 
    } 

Проверка на saveInstanceState == null в случае фрагмента уже присутствует в FragmentManager. В случае восстановления Деятельности из сохраненного состояния экземпляра вы можете использовать:

fragmentManager.findFragmentByTag(String tag) 

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

Лучшим подходом может стать отказ от инъекции вашего фрагмента внутри вашей деятельности и использование статических фабрик, как рекомендовано в Руководстве разработчика. Затем вы введете зависимости для вашего фрагмента внутри самого фрагмента.Что-то вроде этого:

class MeasurementFragment extends Fragment { 

    @Inject Converter converter; 

    static MeasurementFragment instantiate() { 
     //if MeasurementFragment comes to require params in the future 
     //you can pass them in here and use setArguments(Bundle params); 
     return new MeasurementFragment(null); 
    } 

    MeasurementFragment(){ 
    } 

    @Override 
    public void onActivityCreated(Bundle savedInstanceState) { 
     super.onActivityCreated(savedInstanceState); 
     DaggerMainActivityComponent.builder() 
      .build() 
      .inject(this); 
    } 
} 

Теперь, когда вы используете fragmentManager.beginTransaction().add() на MeasurementFragment внутри вашей деятельности, инъекции будут выполняться на коммит, когда Android система применит жизненным циклом обратных вызовов до onActivityCreated(Bundle bundle).

Вот что ваша активность может выглядеть с таким подходом:

class MainActivity extends AppCompatActivity { 

    public static final String MEASUREMENT_FRAG = "MEASUREMENT_FRAG"; 

    MeasurementFragment measureFrag; 

    @Override 
    protected void OnCreate(Bundle b){ 
     super(b); 
     injectMembers(); 
     if (b == null) { 
      measureFrag = MeasurementFragment.instantiate(); 
      getFragmentManager().beginTransaction().add(R.id.frag_container, measureFrag, MEASUREMENT_FRAG).commit(); 
     } 
     else { 
      //frag is already instantiated and added to container 
      measureFrag = getFragmentManager().findFragmentByTag(MEASUREMENT_FRAG); 
     } 
    } 

    @VisibleForTesting 
    void injectMembers() { 
     DaggerMainActivityComponent().create().inject(this); 
    } 
} 

То, что я изложил здесь подход к инъекциям членов внутри фрагментов, что демонстрируется в этом Dagger 2 example on Github. Еще один шаг заключается в том, чтобы сделать Фрагменты максимально легкими и полностью избегать инъекции зависимостей внутри них, как показано в Google Android Architecture Blueprint for Dagger 2.

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