web-dev-qa-db-ja.com

CustomView依存性注入とダガー2(アクティビティスコープ内)

私の質問は this に似ています。

したがって、たとえば、私はLiveData実装を持っています:

public class CustomLiveData extends LiveData<SomeEvent> {

    @Inject
    public CustomLiveData(@ActivityContext Context context) {
        //....
    }

}

カスタムビューに注入したいこと:

public class CustomView extends View {
   @Inject
   SomeApplicationProvider anyProvider;

   @Inject
   CustomLiveData dataProvider; 
   // Getting @com.di.qualifiers.ActivityContext  Android.content.Context cannot be provided without an @Provides-annotated method. 
   // @com.di.qualifiers.ActivityContext Android.content.Context is injected at com.repositories.CustomLiveData.<init>(context)
   // com.repositories.CustomLiveData is injected at com.ui.CustomView.dataProvider com.ui.CustomView is injected at 
   // com.di.ApplicationComponent.inject(view)

   public CustomView(Context context) { this(context, null); }
   public CustomView(Context AttributeSet attrs) { 
      super(context, attrs);

      // Works ok for application provider
      Application.getComponent(context).inject(this);
   }
}

そして、これが残りのDIクラスです。

@ApplicationScope
@Component(
        modules = {AndroidInjectionModule.class,
                ActivityBuilder.class
        })

public interface ApplicationComponent extends AndroidInjector<MyApp> {

    void inject(MyApp application);

    void inject(CustomView view);

    @Component.Builder
    abstract class Builder extends AndroidInjector.Builder<MyApp> {

        public abstract ApplicationComponent build();
    }
}

@ActivityScope
@Module (subcomponents = MainActivitySubcomponent.class)
public abstract class ActivityBuilder {

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

    //...

}


@Subcomponent(modules = {MainActivityModule.class})
public interface MainActivitySubcomponent extends AndroidInjector<MainActivity> {

    @Subcomponent.Builder
    abstract class Builder extends AndroidInjector.Builder<MainActivity> {

    }
}

@ActivityScope
@Module
public class MainActivityModule {

    @Provides
    @ActivityContext
    public Context provideActivityContext(MainActivity activity) {
        return activity;
    }

    // Seems to be wrong or not enough!?
    @Provides
    public CustomLiveData provideCustomLiveData(@ActivityContext Context context) {
        return new CustomLiveData(context);
    }
}


@Qualifier
public @interface ActivityContext{
}

CustomLiveDataがビューではなくMainActivityに注入された場合、コンパイラの問題は発生しません。ありがとう!

16
sinek

tl; drカスタムViewオブジェクト内にモデルレイヤーの依存関係を挿入しません

Viewのサブクラスは、ダガー2インジェクションの適切なターゲットではありません。 Viewオブジェクトは描画するためのものであり、他のものを描画する必要はないため、「ビュー」という名前です。 View のコンストラクタは、これを明確にする必要があります。これらは、XMLで指定された属性からViewオブジェクトを膨らませるように設計されています。つまり、Viewオブジェクトを_layout.xml_ファイルで指定し、ライフサイクルの適切な時点で膨張させ、findViewById(int id)を使用して取得できるようにする必要があります- バターナイフ またはデータバインディング。このように、最高のカスタムViewオブジェクトは依存関係を持ちません。

Viewとモデルレイヤーの一部のデータをリンクする場合、標準パターンはRecyclerViewListViewのようなアダプターを作成することです。これが不可能な場合は、コンストラクターのモデルレイヤーから依存関係を渡すか、Viewのライフサイクルメソッドの1つからインジェクションを要求するよりも、セッター(setData())を使用することをお勧めします。 。

代わりに、LiveDataクラスを使用してアクティビティまたはフラグメント内にAndroidInjectorオブジェクトを挿入すると、何もしなくても正しいContextが提供されます。これは、「CustomLiveDataがビューではなくMainActivityに注入された場合、コンパイラの問題は発生しない」というコメントを説明しています。

LiveDataオブジェクトをアクティビティに注入したら、上記のメソッドの1つ(アダプターまたはセッター)を使用して、データをカスタムViewに関連付けます。 Googleをご覧くださいAndroidアーキテクチャの例 こちら ここでは、モデルレイヤーの要素がDagger 2を使用して注入され、次にListViewと関連付けられますfindViewByIdおよびsetAdapter()

Viewオブジェクトの注入が説明されているDagger 2の問題へのリンク:

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

36
David Rawson

Dagger階層は次のようになります:appcomponent-> activitycomponent

contextに直接依存するアクティビティappcomponentをビュー内に挿入しようとしています。

appcomponentにアクティビティコンテキストを提供できるメソッドがないため、これは不可能です。代わりに、ビュー内で(getContextを使用して)アクティビティを取得し、そこからactivitycomponentを抽出して、CustomLiveDataを注入する必要があります。

1
dominik4142