Кинжал 2 на Android: вводят ту же зависимость в Activity и сохраненный фрагмент

У меня есть объекты классов F1 и F2, которые я хочу ввести в сохраненный фрагмент. У меня также есть объект класса A, который зависит от Activity, и я хочу, чтобы он был введен в эту активность и в сохраненном фрагменте, прикрепленном к этому Диспетчер фрагментов активности. Я пишу следующий код. Во-первых, модуль зависимости активности:

@Module
public class MainActivityModule {
    private Activity mActivity;

    public MainActivityModule(Activity activity) {
        mActivity = activity;
    }

    @Provides
    @ActivityScope
    public A provideA() {
        return new A(mActivity);
    }
}

Затем соответствующий компонент, который должен сделать объект A доступным для его зависимых компонентов:

@ActivityScope
@Component(modules = {MainActivityModule.class})
public interface MainActivityComponent {
    void inject(MainActivity activity);

    // make the A object available to dependent components
    A getA();
}

Я также пишу модуль, связанный с фрагментами:

@Module
public class FragmentModule {
    @Provides
    @FragmentScope
    public F1 provideF1() {
        return new F1();
    }

    @Provides
    @FragmentScope
    public F2 provideF2() {
        return new F2();
    }
}

и его соответствующей составляющей:

@FragmentScope
@Component(modules = {FragmentModule.class}, dependencies = {MainActivityComponent.class})
public interface FragmentComponent {
    void inject(MyFragment presenter);
}

Наконец, я добавляю зависимость от A в Activity, где мне также нужно называть конкретные методы жизненного цикла. Активность также предоставляет метод для получения компонента, чтобы Фрагмент мог использовать его при построении своего собственного компонента:

// in MainActivity.onCreate
mActivityComponent = DaggerMainActivityComponent.builder()
        .mainActivityModule(new MainActivityModule(this))
        .build();
mActivityComponent.inject(this);
mA.onCreate();

и я пытаюсь добавить в Фрагмент зависимости от A, F1, F2:

// in MyFragment.onCreate
FragmentComponent component = DaggerFragmentComponent.builder()
        .fragmentModule(new FragmentModule())
        .mainActivityComponent(((MainActivity) getActivity()).getComponent())
        .build();
component.inject(this);

Однако, поскольку Fragment сохраняется, когда Activity уничтожается и воссоздается системой, реагирующей на изменение конфигурации (например, вращение устройства), фрагмент поддерживает ссылку на старый экземпляр A, в то время как новый Activity правильно воссоздал новый экземпляр A, чтобы пойти с ним. Чтобы обойти эту проблему, мне нужно создать FragmentComponent и ввести зависимости в MyFragment.onActivityCreated, а не MyFragment.onCreate. С другой стороны, это означает, что зависимости F1 и F2 воссоздаются каждый раз, когда активность разрушается и воссоздается; но они являются зависимыми от фрагментации, поэтому они должны следовать жизненному циклу фрагмента, а не активности.

Таким образом, мой вопрос таков: возможно ли иметь разностные зависимости, введенные в сохраненный фрагмент? В идеале зависимости F1 и F2 должны быть введены в MyFragment.onCreate, тогда как зависимость A должна быть введена в MyFragment.onActivityCreated. Я попытался использовать два разных компонента, но, похоже, не удалось выполнить частичную инъекцию. В настоящее время я закончил тем, что добавил явное переназначение зависимостей Fragment A в MyFragment.onActivityCreated, но это не действительно инъекция, вы знаете. Может ли это быть сделано лучше?

Ответы

Ответ 1

Учитывая, что ваш оставшийся фрагмент живет дольше, чем ваша активность, я бы сказал, что правильным способом сделать это будет то, что FragmentScope содержит ActivityScope, а не наоборот.

Значение вашего FragmentComponent будет

@FragmentScope
@Component(modules = {FragmentModule.class})
public interface FragmentComponent {
    void inject(MyFragment presenter);
}

И ваш компонент Activity имел бы

@ActivityScope
@Component(dependencies = {FragmentComponent.class}, modules = {MainActivityModule.class})
public interface MainActivityComponent extends FragmentComponent { //provision methods
    void inject(MainActivity activity);

    // make the A object available to dependent components
    A getA();
}

Это возможно, если ваши введенные классы Fragment не зависят от модуля Activity как зависимостей.

Это можно сделать с чем-то вроде

public class MainActivity extends AppCompatActivity {

    private MainActivityComponent mainActivityComponent;

    private MyFragment myFragment;

    @Override
    public void onCreate(Bundle saveInstanceState) {
         super.onCreate(saveInstanceState);
         setContentView(R.layout.activity_main);

         if(saveInstanceState == null) { // first run
             myFragment = new MyFragment(); //headless retained fragment
             getSupportFragmentManager()
                .beginTransaction()
                .add(myFragment, MyFragment.class.getName()) //TAG
                .commit();
         } else {
             myFragment = (MyFragment)(getSupportFragmentManager()
                               .findFragmentByTag(MyFragment.class.getName()));
         }
    }

    @Override
    public void onPostCreate() {
         mainActivityComponent = DaggerMainActivityComponent.builder()
              .fragmentComponent(myFragment.getComponent())
              .build();
    }
}

и

public class MyFragment extends Fragment {
    public MyFragment() {
         this.setRetainInstance(true);
    }

    private FragmentComponent fragmentComponent;

    @Override
    public void onCreate(Bundle saveInstanceState) {
        super.onCreate(saveInstanceState);
        this.fragmentComponent = DaggerFragmentComponent.create();
    }

    public FragmentComponent getFragmentComponent() {
        return fragmentComponent;
    }
}

EDIT:

public class MyFragment extends Fragment {
    public MyFragment() {
         this.setRetainInstance(true);
         this.fragmentComponent = DaggerFragmentComponent.create();
    }

    private FragmentComponent fragmentComponent;

    public FragmentComponent getFragmentComponent() {
        return fragmentComponent;
    }
}

public class MainActivity extends AppCompatActivity {

    private MainActivityComponent mainActivityComponent;

    private MyFragment myFragment;

    @Inject
    A mA;

    @Override
    public void onCreate(Bundle saveInstanceState) {
         super.onCreate(saveInstanceState);
         setContentView(R.layout.activity_main);

         if(saveInstanceState == null) { // first run
             myFragment = new MyFragment(); //headless retained fragment
             getSupportFragmentManager()
                .beginTransaction()
                .add(myFragment, MyFragment.class.getName()) //TAG
                .commit();
         } else {
             myFragment = (MyFragment)(getSupportFragmentManager().findFragmentByTag(MyFragment.class.getName()));
         }
         mainActivityComponent = DaggerMainActivityComponent.builder()
              .fragmentComponent(myFragment.getComponent())
              .build();
         mainActivityComponent.inject(this);
         mA.onCreate();
    }
}