web-dev-qa-db-ja.com

Androidの短剣2:アクティビティと保持されたフラグメントに同じ依存関係を挿入する

保持されているFragmentに注入するクラスF1およびF2のオブジェクトがあります。また、Activityに依存するクラスAのオブジェクトがあり、そのアクティビティと、そのActivityのFragment Managerに接続されている保持されているFragmentに挿入する必要があります。次のコードを書きます。まず、アクティビティ依存関係のモジュール:

@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への依存関係をアクティビティに挿入します。ここで、特定のライフサイクルメソッドを呼び出す必要もあります。アクティビティはまた、コンポーネントを取得するメソッドを提供し、フラグメントが独自のコンポーネントを構築するときにそれを使用できるようにします。

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

そして、フラグメント内のAF1F2への依存関係も注入しようとします:

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

ただし、フラグメントが保持されるため、構成の変更(デバイスのローテーションなど)に反応してシステムによってアクティビティが破棄および再作成されると、フラグメントは古いAインスタンスへの参照を維持しますが、新しいアクティビティはそれに伴う新しいAインスタンスを正しく再作成しました。この問題を回避するには、FragmentComponentを作成し、MyFragment.onActivityCreatedではなくMyFragment.onCreateに依存関係を挿入する必要があります。一方、これは、アクティビティが破棄されて再作成されるたびに、F1およびF2依存関係が再作成されることを意味します。ただし、これらはフラグメントスコープの依存関係であるため、アクティビティではなくフラグメントライフサイクルに従う必要があります。

したがって、私の質問は次のとおりです。保持されたフラグメントに異なるスコープの依存関係を挿入することは可能ですか?理想的には、F1およびF2の依存関係をMyFragment.onCreateに挿入し、Aの依存関係をMyFragment.onActivityCreatedに挿入する必要があります。 2つの異なるコンポーネントを使用してみましたが、部分的な注入を実行できないようです。現在、私はMyFragment.onActivityCreatedにFragment A依存関係の明示的な再割り当てを追加することになりましたが、それは実際には注入ではありません。これはもっと良い方法でできますか?

15

保持されたフラグメントの寿命がアクティビティよりも長いことを考えると、これを行う適切な方法はFragmentScopeActivityScopeを含めることであり、その逆ではないことを期待します。

あなたのFragmentComponentが持つことを意味します

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

そしてあなたのアクティビティコンポーネントは

@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;
    }
}

編集:

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();
    }
}
8
EpicPandaForce