作为后端软件开发人员,我习惯于使用Spring作为我最喜欢的依赖注入引擎。 替代方案包括Java EE的CDI ,它以不同的方式获得相同的结果。 但是,两者都在运行时注入:这意味着在应用程序启动时(要满足所有依赖关系所需的时间)要付出一定的性能成本。 在应用程序服务器上,以天数(如果不是几周)来衡量应用程序寿命,则可以接受启动时间开销。 如果服务器只是大型群集中的一个节点,则甚至完全透明。
作为Android用户,我对启动应用程序感到不满意,并且在打开之前它会滞后几秒钟。 如果我们再增加几秒钟的时间,那么对用户友好性将是非常糟糕的。 更糟糕的是,DI引擎的内存消耗将是一场灾难。 这就是Square开发出一种称为Dagger的编译时依赖注入机制的原因。 请注意,Google目前正在开发Dagger 2 。 在继续之前,我必须承认Dagger 2的文档简明扼要-充其量。 但这是另一篇博客文章的绝佳机会:-)
Dagger 2与注释处理器一起使用:编译时,它将分析您的注释代码并在您的组件之间生成接线代码。 好处是该代码与手动编写的代码非常相似,没有秘密的黑魔法(与运行时DI及其代理相对)。 以下代码显示要注入的类:
publicclassTimeSetListenerimplementsTimePickerDialog.OnTimeSetListener{
privatefinalEventBuseventBus;
publicTimeSetListener(EventBuseventBus){
this.eventBus=eventBus;
}
@Override
publicvoidonTimeSet(TimePickerview,inthourOfDay,intminute){
eventBus.post(newTimeSetEvent(hourOfDay,minute));
}
}
注意,代码在各个方面都完全独立于Dagger。 人们无法推断出最终将如何注入它。 有趣的部分是如何使用Dagger注入所需的eventBus
依赖项。 分两个步骤:
1.在上下文中获取对eventBus
实例的引用1.使用相关参数调用构造函数
接线配置本身是在所谓的模块中完成的:
@Module
publicclassApplicationModule{
@Provides
@Singleton
publicTimeSetListenertimeSetListener(EventBuseventBus){
returnnewTimeSetListener(eventBus());
}
...
}
请注意, EventBus
作为参数传递给方法,由提供它的上下文决定。 同样,作用域是@Singleton
。
与工厂的绑定发生在一个组件中 ,该组件引用了所需的模块(或更多模块):
@Component(modules=ApplicationModule.class)
@Singleton
publicinterfaceApplicationComponent{
TimeSetListenertimeListener();
...
}
这是非常简单的...直到有人注意到Android中的某些对象(如果不是大多数)具有由Android本身管理的生命周期,而无需调用我们的注入友好构造函数 。 活动就是这样的对象:它们是由框架实例化和启动的。 只有通过像onCreate()
这样的专用生命周期方法,我们才能将代码连接到对象中。 由于必须进行电场注入,因此该用例看起来要糟糕得多。 更糟糕的是,还需要调用Dagger:在这种情况下,它充当普通工厂。
publicclassEditTaskActivityextendsAbstractTaskActivity{
@InjectTimeSetListenertimeListener;
@Override
protectedvoidonCreate(BundlesavedInstanceState){
DaggerApplicationComponent.create().inject(this);
}
...
}
我们第一次看到与Dagger的耦合,但这是一个很大的耦合。 什么是DaggerApplicationComponent
? 前一个ApplicationComponent
的实现,以及提供它们实例的工厂。 而且由于它不提供inject()
方法,因此我们必须在接口中声明它:
@Component(modules=ApplicationModule.class)
@Singleton
publicinterfaceApplicationComponent{
TimeSetListenertimeListener();
voidinject(EditTaskActivityeditTaskActivity);
...
}
作为记录,生成的类如下所示:
@Generated("dagger.internal.codegen.ComponentProcessor")
publicfinalclassDaggerApplicationComponentimplementsApplicationComponent{
privateProvider<TimeSetListener>timeSetListenerProvider;
privateMembersInjector<EditTaskActivity>editTaskActivityMembersInjector;
...
privateDaggerApplicationComponent(Builderbuilder){
assertbuilder!=null;
initialize(builder);
}
publicstaticBuilderbuilder(){
returnnewBuilder();
}
publicstaticApplicationComponentcreate(){
returnbuilder().build();
}
privatevoidinitialize(finalBuilderbuilder){
this.timeSetListenerProvider=ScopedProvider.create(ApplicationModule_TimeSetListenerFactory
.create(builder.applicationModule,eventBusProvider));
this.editTaskActivityMembersInjector=TimeSetListener_MembersInjector
.create((MembersInjector)MembersInjectors.noOp(),timeSetListenerProvider);
}
@Override
publicEventBuseventBus(){
returneventBusProvider.get();
}
@Override
publicvoidinject(EditTaskActivityeditTaskActivity){
editTaskActivityMembersInjector.injectMembers(editTaskActivity);
}
publicstaticfinalclassBuilder{
privateApplicationModuleapplicationModule;
privateBuilder(){}
publicApplicationComponentbuild(){
if(applicationModule==null){
this.applicationModule=newApplicationModule();
}
returnnewDaggerApplicationComponent(this);
}
publicBuilderapplicationModule(ApplicationModuleapplicationModule){
if(applicationModule==null){
thrownewNullPointerException("applicationModule");
}
this.applicationModule=applicationModule;
returnthis;
}
}
}
没有免费的午餐。 乍看之下,尽管编译时DI非常吸引人,但在生命周期不受我们的代码管理的对象上使用时,它的影响要小得多。 缺点显而易见:耦合到DI框架,更重要的是增加了对单元进行单元测试的难度。 但是,考虑到Android的限制,这可能是最好的选择。
翻译自: https://blog.frankel.ch/compile-time-dependency-injection-tradeoffs-in-android/