当我想将每个活动中的一些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.但是我遇到了一些问题.我想也许用泛型我可以做到,但我不确切知道如何.
这是一个问题:inject
您调用哪种方法,Java可以在编译时确定?
如"关于协方差的注释"中所述,Dagger将为您定义的任何成员注入方法生成代码,但仅为您传入的静态类型生成代码.
@Component public interface YourComponent { void injectBase(BaseActivity baseActivity); void injectA(ActivityA activityA); void injectB(ActivityB activityB); void injectC(ActivityC activityC); }
调用injectA
并传递ActivityA实例时,您将获得ActivityA中定义的字段的注入,包括BaseActivity中的字段.与ActivityB和ActivityC相同.但是,如果您调用injectBase
,Dagger将仅注入属于BaseActivity的字段,即使您传入的对象恰好是ActivityA,ActivityB或ActivityC.Dagger在编译时生成代码,因此如果调用injectBase
,注入只会发生在BaseActivity上的字段上 - 因为这是为BaseActivity的成员注入器生成的代码,而这些是Dagger知道如何注入BaseActivity参数的唯一字段.
当然,因为BaseActivity只知道它this
是BaseActivity的子类型,所以它只能调用injectBase
而不能调用任何特定的子类型.重要的是,这仍然是正确的,即使所有的名字injectBase
,injectA
等等,都是相同的(像inject
).JVM将选择它在编译时可以确定的最窄的重载inject(BaseActivity)
,这将引入BaseActivity的字段而不是子类型.如果你要将它们命名为唯一的,你会看到你正在调用哪一个,以及它为什么不注入子类型字段.
泛型在这里没有帮助:您正在寻找您的Component来生成并调用ActivityA,ActivityB和ActivityC的成员注入器.泛型将被删除,但此外组件不能采用BaseActivity的任意子类:Dagger无法在编译时为其可能仅在运行时遇到的类型生成代码.你真的需要在编译时在Dagger中准备这些类型.
解决这个问题的一种方法是允许子类型自我注入.子类型知道this
ActivityA(等等),即使代码看起来与字符字符相同,Java也可以识别正确的类型并正确编译它.
// in BaseActivity protected abstract void injectDependencies(); // in ActivityA @Override protected void injectDependencies() { component.injectA(this); }
然而,还有另一种最近发布的选项,使用dagger.android,它采用Multibindings和(有效的)一个Map
以动态地注入所需的特定类型.这也适用于超类,以至于您可以让您的Activity扩展DaggerActivity,并且所有内容都可以按您喜欢的方式工作.(请参阅dagger.android.support包以获取与AppCompatActivity等效的DaggerAppCompatActivity.)