使用MVP/Dagger 2注入Presenter时的冗余代码优化咨询
我完全懂你这种被大量重复的页面Component和Module代码淹没的痛苦——Dagger 2如果没摸透它的进阶用法,确实容易陷入“为了DI反而写更多代码”的尴尬。结合你的架构设计(AppComponent -> FeatureXComponent -> ScreenXComponent),这里有几个针对性的优化方向,帮你砍掉冗余:
1. 用Dagger Android的@ContributesAndroidInjector彻底消除页面Component/Module
这是最直接的解决方案,Dagger Android专门为Android组件(Activity/Fragment)设计了这个注解,能自动生成页面所需的注入逻辑,完全不用手动写ScreenComponent和ScreenModule。
具体步骤:
- 确保项目引入Dagger Android依赖(如果还没加的话)。
- 在Feature级Module(比如
Feature1Module)中添加抽象方法,用@ContributesAndroidInjector标注对应的页面组件:
@Module public abstract class Feature1Module { // 自动为Screen1Fragment生成注入所需的子组件和绑定逻辑 @ContributesAndroidInjector abstract Screen1Fragment contributeScreen1Fragment(); @ContributesAndroidInjector abstract Screen2Fragment contributeScreen2Fragment(); }
- 将这个FeatureModule添加到对应
FeatureComponent的modules列表中。 - 在Application类中实现
HasAndroidInjector,提供DispatchingAndroidInjector:
public class MyApplication extends Application implements HasAndroidInjector { @Inject DispatchingAndroidInjector<Object> androidInjector; private AppComponent appComponent; @Override public void onCreate() { super.onCreate(); appComponent = DaggerAppComponent.create(); appComponent.inject(this); } @Override public AndroidInjector<Object> androidInjector() { return androidInjector; } public Feature1Component getFeature1Component() { return appComponent.feature1ComponentBuilder().build(); } }
- 最后在Fragment的
onAttach或Activity的onCreate中调用注入:
@Override public void onAttach(Context context) { super.onAttach(context); AndroidInjection.inject(this); }
这样所有页面级的Component和Module都可以删掉,Dagger会自动处理注入逻辑——包括View实例的传递(如果Presenter需要View,直接在Presenter构造函数中注入Screen1Contract.View,Dagger会自动关联当前Fragment/Activity实例)。
2. 用@BindsInstance替代页面级Module传递View
如果暂时不想引入Dagger Android,也可以通过@BindsInstance砍掉每个页面的Module代码。
具体调整:
- 修改页面级Component的定义,添加带
@BindsInstance的Builder:
@Subcomponent public interface Screen1Component { void inject(Screen1Fragment fragment); @Subcomponent.Builder interface Builder { // 直接通过Builder传入View实例,无需Module @BindsInstance Builder view(Screen1Contract.View view); Screen1Component build(); } }
- 在FeatureComponent中添加获取页面Component Builder的方法:
@Feature @Subcomponent(modules = Feature1Module.class) public interface Feature1Component { Screen1Component.Builder screen1ComponentBuilder(); Screen2Component.Builder screen2ComponentBuilder(); @Subcomponent.Builder interface Builder { Feature1Component build(); } }
- 注入代码简化为:
((MyApplication) getActivity().getApplication()) .getFeature1Component() .screen1ComponentBuilder() .view(this) // 直接传View实例,不用new Module .build() .inject(this);
这个方案砍掉了每个页面的Module,仅保留Component(当然还是Dagger Android的方案更彻底)。
3. 封装Base类统一注入逻辑
不管用上面哪种方案,都可以通过BaseFragment/BaseActivity封装注入代码,避免每个页面重复写注入调用:
public abstract class BaseDaggerFragment extends Fragment { @Override public void onAttach(Context context) { super.onAttach(context); injectDependencies(); } protected abstract void injectDependencies(); }
然后每个页面Fragment只需实现injectDependencies:
public class Screen1Fragment extends BaseDaggerFragment implements Screen1Contract.View { @Inject Screen1Presenter presenter; @Override protected void injectDependencies() { // 若用Dagger Android: AndroidInjection.inject(this); // 若用@BindsInstance方案: ((MyApplication) getActivity().getApplication()) .getFeature1Component() .screen1ComponentBuilder() .view(this) .build() .inject(this); } }
关于生命周期的顾虑
你担心页面生命周期比Feature短的问题完全不用担心:
- 页面级的子组件(自动生成或手动定义的)生命周期与页面绑定,页面销毁时子组件会被回收,不会持有Feature级实例的引用导致内存泄漏。
- Feature级组件的
@Feature作用域保证其内部实例在Feature生命周期内是单例,页面子组件依赖这些实例时,仅持有引用,不会影响生命周期。
内容的提问来源于stack exchange,提问作者amitavk

