当我想将每个活动中的一些Dagger 2样板代码移动到BaseActivity时,我遇到了一些麻烦。
BaseActivity extends AppCompatActivity
我有多个活动,例如:
ActivityA extends BaseActivity implements InterfaceA;
ActivityB extends BaseActivity implements InterfaceB;
...
在每个活动中,我都有这样的方法(其中X是A,B,C,......用于每个活动):
public void initActivity() {
ComponentX compX;
...
compX = appComponent.plus(new ModuleX(this)); // this == InterfaceX
...
compX.inject(this); // this == ActivityX
}
我试图减少此代码,将其移至父BaseActivity。但是我遇到了一些问题。我想也许用泛型我可以做到,但我不确切知道如何。
答案 0 :(得分:5)
以下是问题:您调用了哪个@Component public interface YourComponent {
void injectBase(BaseActivity baseActivity);
void injectA(ActivityA activityA);
void injectB(ActivityB activityB);
void injectC(ActivityC activityC);
}
方法,Java可以在编译时确定吗?
如"A note about covariance"中所述,Dagger将为您定义的任何成员注入方法生成代码,但仅您传入的静态类型。
injectA
调用injectBase
并传递ActivityA实例时,您将获得ActivityA中定义的字段的注入,包括BaseActivity中的字段。与ActivityB和ActivityC相同。但是,如果您调用injectBase
,Dagger将仅注入属于BaseActivity的字段,即使您传入的对象恰好是ActivityA,ActivityB或ActivityC。 Dagger在编译时生成代码,因此如果调用this
,则只会对BaseActivity上的字段进行注入 - 因为这是为BaseActivity的成员注入器生成的代码,而这些是Dagger知道如何的唯一字段注入BaseActivity参数。
当然,因为BaseActivity只知道injectBase
是BaseActivity的子类型,所以它只能调用injectBase
而不能调用任何特定的子类型。重要的是,即使所有名称injectA
,inject
等都是相同的(如inject(BaseActivity)
),这仍然是正确的。 JVM将选择它在编译时可以确定的最窄的重载,它将是this
,它将注入BaseActivity的字段而不是子类型。如果你要对它们进行唯一命名,你会看到你正在调用哪一个,以及它为什么不注入子类型字段。
泛型在这里没有帮助:您正在寻找您的Component来生成并调用ActivityA,ActivityB和ActivityC的成员注入器。泛型将被删除,但此外组件不能采用BaseActivity的任意子类:Dagger无法在编译时为其可能仅在运行时遇到的类型生成代码。你真的需要在编译时在Dagger中准备这些类型。
解决这个问题的一种方法是允许子类型自我注入。子类型知道// in BaseActivity
protected abstract void injectDependencies();
// in ActivityA
@Override protected void injectDependencies() { component.injectA(this); }
是ActivityA(依此类推),即使代码看起来与字符字符相同,Java也可以识别正确的类型并正确编译它。
Map<Class, MembersInjector>
然而,还有另一个最近发布的选项,使用dagger.android,它使用Multibindings和(有效){{1}}到动态注入您想要的特定类型。这也适用于超类,也可以让你的Activity扩展DaggerActivity,一切都会按你喜欢的方式工作。 (请参阅dagger.android.support包,了解您的AppCompatActivity等效DaggerAppCompatActivity。)