如何让带id构造参数的ViewModelFactory可通过Dagger注入?
我在Android开发中使用ViewModel结合Dagger管理依赖,我的ViewModel在创建时需要从MainActivity获取id。现有代码如下:
Activity中的实现:
@Inject ViewModelProviderFactory mViewModelFactory; ViewModelProviders.of(this, mViewModelFactory).get(MyViewModel.class);
ViewModelProviderFactory实现:
public class ViewModelProviderFactory implements ViewModelProvider.Factory { private int id; @Inject public ViewModelProviderFactory(int id) { this.id = id; } @Override public <T extends ViewModel> T create(Class<T> modelClass) { return new MyViewModel(id); } }
对应的Dagger Module:
@Module public class MainActivityModule { int id; public MainActivityModule(int id){ this.id = id; } @Provides ViewModelProvider.Factory mainViewModelProvider() { return new ViewModelProviderFactory<>(id); } }
现在的困扰是:如果用新版Dagger的方式,为每个Activity创建子组件、Builder会产生大量冗余代码。我想知道有没有更简洁的方案?或者直接在Activity中通过new创建Factory更合适,比如:
ViewModelProviders.of(this, new ViewModelProviderFactory(id)).get(MyViewModel.class);
我来帮你梳理一下这个问题的解决方案,结合Android开发中ViewModel+Dagger的常见实践,有几个不同的思路可以选:
方案1:直接new Factory——简单但有局限
如果你的ViewModelProviderFactory只需要传入这个运行时的id,不需要注入其他依赖(比如Repository、网络服务等),那直接在Activity里new确实是最简洁的方案。没有多余的Dagger配置,代码直观易懂,完全能满足需求。
但要注意:如果后续你的Factory需要注入其他依赖,这种方式就会变得繁琐——你得手动把这些依赖从Dagger中拿出来再传入Factory,违背了依赖注入“解耦”的初衷。
方案2:用Dagger的Assisted Injection(推荐)
当你需要同时处理编译时注入的依赖(比如由Dagger提供的Repository)和运行时参数(比如Activity里的id)时,Dagger的Assisted Injection是最优解,能彻底避免为每个参数创建子组件、Builder的冗余代码。
具体步骤如下:
- 首先添加Assisted Injection的依赖(记得替换为最新版本):
// Dagger基础依赖 implementation "com.google.dagger:dagger:2.51.1" annotationProcessor "com.google.dagger:dagger-compiler:2.51.1" implementation "com.google.dagger:dagger-android:2.51.1" annotationProcessor "com.google.dagger:dagger-android-processor:2.51.1" // Assisted Injection依赖 implementation "com.squareup.inject:assisted-inject-annotations-dagger2:1.0.0" annotationProcessor "com.squareup.inject:assisted-inject-processor-dagger2:1.0.0"
- 修改你的
MyViewModel,用@Assisted标记运行时参数,并定义一个辅助Factory接口:
public class MyViewModel extends ViewModel { private final int id; // 示例:假设需要注入一个Repository private final UserRepository userRepository; // 编译时依赖直接用@Inject,运行时参数用@Assisted标记 @Inject public MyViewModel(UserRepository userRepository, @Assisted int id) { this.userRepository = userRepository; this.id = id; } // 定义一个被@AssistedFactory标记的接口,用于创建ViewModel @AssistedFactory public interface Factory { MyViewModel create(int id); } }
- 在Dagger Module中绑定这个Factory:
@Module public abstract class ViewModelModule { @Binds abstract ViewModelProvider.Factory bindViewModelFactory(MyViewModel.Factory factory); }
- 在Activity中使用:
@Inject MyViewModel.Factory viewModelFactory; // 初始化ViewModel时,传入运行时的id MyViewModel viewModel = new ViewModelProvider(this, new AssistedViewModelAdapter<>(viewModelFactory, id)) .get(MyViewModel.class); // 简单的适配器类,把Assisted Factory适配成ViewModelProvider.Factory public class AssistedViewModelAdapter<T extends ViewModel> implements ViewModelProvider.Factory { private final MyViewModel.Factory factory; private final int id; public AssistedViewModelAdapter(MyViewModel.Factory factory, int id) { this.factory = factory; this.id = id; } @SuppressWarnings("unchecked") @Override public <T extends ViewModel> T create(Class<T> modelClass) { return (T) factory.create(id); } }
这样你就不需要为每个Activity创建单独的Module或子组件,同时兼顾了Dagger注入和运行时参数传递。
方案3:用SavedStateHandle(适合需要持久化的参数)
如果这个id需要在屏幕旋转等配置变化后保留,你可以用SavedStateHandle来传递参数:
public class MyViewModel extends ViewModel { private final int id; @Inject public MyViewModel(SavedStateHandle savedStateHandle) { // 从SavedStateHandle中获取id this.id = savedStateHandle.get("USER_ID"); } }
然后在Activity中初始化ViewModel时,用SavedStateViewModelFactory:
// 先把id存入Bundle Bundle bundle = new Bundle(); bundle.putInt("USER_ID", id); MyViewModel viewModel = new ViewModelProvider(this, new SavedStateViewModelFactory(getApplication(), this, bundle)) .get(MyViewModel.class);
这种方式不需要额外的Dagger配置,适合需要持久化的参数场景。
总结
- 场景1:ViewModel仅需简单运行时参数,无其他注入依赖 → 直接
new Factory最省心。 - 场景2:需要结合Dagger注入的依赖和运行时参数 → 优先用Assisted Injection,避免冗余代码。
- 场景3:参数需要在配置变化后保留 → 用
SavedStateHandle。
内容的提问来源于stack exchange,提问作者Siamak Ferdos

