私はAndroidアプリケーションでDIにDagger2を使用します。@ Injectフィールドを使用するすべてのクラスに対してインジェクトメソッドを作成する必要があることがわかりました。すべてのサブクラスでinjectを呼び出す必要はありませんか?たとえば、Activityを使用します。すべてのActivityが拡張されるBaseActivity
があります。コンポーネントにinjectメソッドを作成する方法はBaseActivityとBaseActivityのonCreateでinjectを呼び出すだけで、サブアクティビティの@injectフィールドは自動的に挿入されますか?
私は同じ状況に遭遇しました。すべてのアクティビティの共通コンポーネントからの注入を少し楽にする1つの方法は次のとおりです。
1)Applicationクラスを拡張して、共通コンポーネントを作成し、それへの参照を保持できるようにします。
_public class ApplicationDagger extends Application {
private ApplicationComponent component;
@Override
public void onCreate(){
super.onCreate();
component = DaggerApplicationComponent.builder().applicationModule(new ApplicationModule(this)).build();
}
public ApplicationComponent getComponent(){
return component;
}
}
_
2)Applicationから共通コンポーネントを取得し、抽象メソッドinjectActivity
を呼び出して、引数としてコンポーネントを指定する抽象DaggerActivityを作成します。このような:
_public abstract class DaggerActivity extends Activity {
@Override
public void onCreate(Bundle saved){
super.onCreate(saved);
ApplicationComponent component = ((ApplicationDagger) getApplication()).getComponent();
injectActivity(component);
}
public abstract void injectActivity(ApplicationComponent component);
}
_
3)最後に、各Activity
拡張DaggerActivity
を実際に注入する必要があります。しかし、abstract
メソッドを実装する必要があるため、これは今では少ない労力で実行できます。そうしないと、コンパイルエラーが発生します。さあ:
_public class FirstActivity extends DaggerActivity {
@Inject
ClassToInject object;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//initialize your Activity
}
@Override
public void injectActivity(ApplicationComponent component) {
component.inject(this);
}
}
_
もちろん、コンポーネントで各アクティビティを明示的に宣言する必要があります。
UPDATE:@ActivityScopeオブジェクトをフラグメントに挿入する
ある時点で、オブジェクトをActivity
ライフサイクルにバインドするために カスタムスコープ を使用する必要がありました。一部の人々を助けるかもしれないので、私はこの投稿を拡張することにしました。
@ ModuleクラスActivityModule
と@ SubcomponentインターフェースActivityComponent
があるとします。
DaggerActivity
を変更する必要があります。 Activities
拡張DaggerActivity
は、新しいメソッド(署名の変更)を実装する必要があります。
_public abstract class ActivityDagger extends AppCompatActivity {
ActivityComponent component;
@Override
protected void onCreate(Bundle savedInstanceState) {
component = ((ApplicationDagger) getApplication()).getComponent().plus(new ActivityModule(this));
injectActivity(component);
super.onCreate(savedInstanceState);
}
ActivityComponent getComponent() {
return component;
}
public abstract void injectActivity(ActivityComponent component);
}
_
次に、FragmentDagger
を拡張するクラスFragment
を次のように作成できます。
_public abstract class FragmentDagger extends Fragment {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityDagger activityDagger = (ActivityDagger) getActivity();
ActivityComponent component = activityDagger.getComponent();
injectFragment(component);
}
public abstract void injectFragment(ActivityComponent component);
}
_
Activities
に関しては、Fragments
拡張FragmentDagger
には実装するメソッドが1つしかありません。
_public abstract void injectFragment(ActivityComponent component);
_
Fragments
はどこでも再利用できるはずです。 ActivityDagger
のsuper.onCreated()
メソッドは、コンポーネントのインスタンス化後に呼び出す必要があることに注意してください。それ以外の場合、Activity
のメソッドsuper.onCreate()
が呼び出されるため、Fragment
状態が再作成されるときに NullPointerException が返されます。
リフレクションを使用して少しハックすることができます:
_public class UiInjector {
private static final String METHOD_NAME = "inject";
private final UIComponent component;
public UiInjector(final UIComponent component) {
this.component = component;
}
public void inject(final Object subject) {
try {
component.getClass()
.getMethod(METHOD_NAME, subject.getClass())
.invoke(component, subject);
} catch (final NoSuchMethodException exception) {
throwNoInjectMethodForType(component, subject.getClass());
} catch (final Exception exception) {
throwUnknownInjectionError(exception);
}
}
private void throwNoInjectMethodForType(final Object component, final Class subjectType) {
throw new RuntimeException(component.getClass().getSimpleName() +
" doesn't have inject method with parameter type : " + subjectType);
}
private void throwUnknownInjectionError(final Exception cause) {
throw new RuntimeException("Unknown injection error", cause);
}
}
_
この場合、コンポーネントにinjectメソッドを記述する必要がありますが、各アクティビティ、フラグメント、ビューなどに「inject」メソッドは必要ありません。
なぜ機能するのですか?インジェクションサブジェクトでgetClass()
を使用すると、ベースではなく子孫クラスが取得されます。
あぶない! Proguardを使用する場合は、次の_-keep class <ComponentClass> { *; }
_をルールに追加して、コンポーネントにインジェクトメソッドをそのまま保持する必要があります。