web-dev-qa-db-ja.com

Dagger 2での「HasFragmentInjector」の実際の使用法は何ですか

私は以前dagger2 v2.2を実装しましたが、今のところdagger.Androidの部分も追加しています。それでサンプルプロジェクトを作成しています。

@ Provide@ Modules@ Componentsなどのアノテーションの古い方法論を知っていますが、Dagger 2.8以降からこのAndroidサポートが追加されています@ ActivityKey@ ContributesAndroidInjector@ Subcomponent.Builderなどの新しいインジェクションを含むライブラリ.

だから私の質問はそれがテーブルにもたらす利点です。

基本クラスのInjectメソッドのような問題はすべての子クラスで機能しますか?または他の利点?

2番目の質問-HasFragmentInjectorは、フラグメントマネージャーを使用して行ったように、アクティビティ内にフラグメントを読み込むだけですか?または私はいくつかのことを逃していますか?

ライブラリのドキュメンテーションはそのような答えを提供していないので、すべてのライブラリユーザーのためのより有益な質問に反対票を投じないでください。

16
Wasim K. Memon

最初の質問

Dagger 2.8以降では、_@ActivityKey_、_@ContributesAndroidInjector_、_@Subcomponent.Builder_などの新しい注釈が追加されたこのAndroidサポートライブラリも追加されました。したがって、私の質問は、それが表にもたらすメリットです。

これはすでに DispatchingAndroidInjectorと他のdagger-Androidクラスを使用する利点は何ですか?

すべての子クラスで機能する基本クラスに注入メソッドがないなどの問題を解決しますか?

Dagger 2は、依存性注入のためにコンパイル時にコード生成を使用します。この点で、実行時に注入サイトを検査するGuiceなどの他の依存性注入フレームワークとは異なります。 Dagger 2が機能するためには、ある時点で、注射部位の不変条件を指定する必要があります。したがって、次のようなものを書くことは決してできません。

_void inject(Activity activity);
_

dagger 2コンポーネントの内部にあり、すべてのアクティビティを挿入します。

ただし、dagger-Androidで利用可能な新しいクラスには多くの改善点があります。以前は次のように書く必要があります。

_void inject(MainActivity mainActivity);
_

注入部位ごとに以下のコードを書くことができます。

_@Module(subcomponents = MainActivitySubcomponent.class)
public abstract class MainActivityModule {

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

その後:

_AndroidInjection.inject(this);
_

mainActivity内の適切なポイント。

2番目の質問

HasFragmentInjectorは、FragmentManagerを使用していたように、アクティビティ内にFragmentをロードするだけですか?または何か不足していますか?

HasFragmentInjectorは、FragmentがAndroidInjectorを取得するクラスをマークするだけです。あなたはコードで自分自身を見ることができます GitHub上 for AndroidInjection#inject(Fragment fragment)

_public static void inject(Fragment fragment) {
    checkNotNull(fragment, "fragment");
    HasFragmentInjector hasFragmentInjector = findHasFragmentInjector(fragment);
    Log.d(TAG, String.format(
        "An injector for %s was found in %s",
        fragment.getClass().getCanonicalName(),
        hasFragmentInjector.getClass().getCanonicalName()));

    AndroidInjector<Fragment> fragmentInjector = hasFragmentInjector.fragmentInjector();
    checkNotNull(fragmentInjector,"%s.fragmentInjector() returned null",
    hasFragmentInjector.getClass().getCanonicalName());
    fragmentInjector.inject(fragment);
} 
_

このメソッドは、javadocから、最初に親フラグメント、次にアクティビティ、次にアプリケーションでHasFragmentInjectorを検索し、_AndroidInjector<Fragment>_を使用してフラグメントのフィールドを挿入します。

ただし、HasFragmentInjectorが存在しても、Dagger 2を使用してフラグメントの管理を開始する必要があるという意味ではありません。

_public class MainActivity {

     @Inject CoffeeFragment coffeeFragment; //no! don't do this
     @Inject TeaFragment teaFragment; //no!
_

静的ファクトリメソッドを使用しているFragmentsをインスタンス化する慣用的な方法を使用する必要があります。 Dagger 2は、トランザクションを使用してFragmentを追加したり、ViewPagerに委任したりするときにonAttach(Context context)が呼び出されたときに、Fragments内のフィールドに対して注入を実行します。上記の例の代わりに、次のコードは、ViewPagerと2つのフラグメントを持つ非常に単純なアクティビティです。

_public class MainActivity extends AppCompatActivity implements HasSupportFragmentInjector {

    @Inject
    DispatchingAndroidInjector<Fragment> fragmentDispatchingAndroidInjector;

    ViewPager mViewPager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        AndroidInjection.inject(this);
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        BeveragesPagerAdapter beveragesPagerAdapter = new BeveragesPagerAdapter(getSupportFragmentManager());
        mViewPager = (ViewPager) findViewById(R.id.viewpager);
        mViewPager.setAdapter(beveragesPagerAdapter);
    }

    class BeveragesPagerAdapter extends FragmentStatePagerAdapter {

        public BeveragesPagerAdapter(FragmentManager fm) {
            super(fm);
        }

        @Override
        public Fragment getItem(int i) {
            switch (i) {
                case 0:
                    return TeaFragment.instantiate(new Bundle());
                case 1:
                    return CoffeeFragment.instantiate(new Bundle());
                default:
                    throw new IllegalStateException();
            }
        }

        @Override
        public int getCount() {
            return 2;
        }

        @Override
        public CharSequence getPageTitle(int position) {
            return "tab " + (position + 1);
        }
    }

    @Override
    public AndroidInjector<Fragment> supportFragmentInjector() {
        return fragmentDispatchingAndroidInjector;
    }
}
_

FragmentStatePagerAdapterはフラグメントの管理を正しく処理し、MainActivity内のフィールドとして挿入しません。

フラグメント自体は次のようになります。

CoffeeFragment.Javaで:

_public class CoffeeFragment extends Fragment {

    public static CoffeeFragment instantiate(@Nullable Bundle arguments) {
        CoffeeFragment coffeeFragment = new CoffeeFragment();
        coffeeFragment.setArguments(arguments);
        return coffeeFragment;
    }

    @Inject
    @Named("Coffee")
    Repository repository;

    TextView textView;

    @Override
    public void onAttach(Context context) {
        AndroidSupportInjection.inject(this);
        super.onAttach(context);
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View v = inflater.inflate(R.layout.fragment_coffee, container, false);
        textView = (TextView) v.findViewById(R.id.coffee_textview);
        return v;
    }

    @Override
    public void onResume() {
        textView.setText(repository.retrieve());
    }
}
_

CoffeeFragmentModule.Javaで:

_@Module(subcomponents = CoffeeFragmentSubcomponent.class )
public abstract class CoffeeFragmentModule {

    @Binds
    @Named("Coffee")
    abstract Repository repository(CoffeeRepository coffeeRepository);

    @Binds
    @IntoMap
    @FragmentKey(CoffeeFragment.class)
    abstract AndroidInjector.Factory<? extends Fragment> bindCoffeeFragmentInjectorFactory(CoffeeFragmentSubcomponent.Builder builder);
}
_

CoffeeFragmentSubcomponent.Java内:

_@Subcomponent
public interface CoffeeFragmentSubcomponent extends AndroidInjector<CoffeeFragment> {

    @Subcomponent.Builder
    abstract class Builder extends AndroidInjector.Builder<CoffeeFragment> {}
}
_

CoffeeRepository.Java内:

_public class CoffeeRepository implements Repository {

    @Inject
    public CoffeeRepository() {
    }

    @Override
    public String retrieve() {
        return "Coffee!!!!";
    }
}
_
28
David Rawson

公式ドキュメント 私の意見では、このトピックについて非常によく説明しています。

とにかく主な利点は、このようなものの代わりに

((SomeApplicationBaseType) getContext().getApplicationContext())
    .getApplicationComponent()
    .newActivityComponentBuilder()
    .activity(this)
    .build()
    .inject(this);

これを書くだけで、誰でも簡単に生活できます。

AndroidInjection.inject(this);
  1. ボイラープレートが少なく、メンテナンスが容易です。

  2. 以前のアプローチは、依存性注入の基本概念を打ち破るようなものであり、クラスは依存性が注入される方法についての詳細を知らないはずです。

2
Andrej Jurkin