You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让带id构造参数的ViewModelFactory可通过Dagger注入?

问题:Android中ViewModel结合Dagger管理依赖时,如何简化带Activity传入参数的Factory实现?

我在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的冗余代码。

具体步骤如下:

  1. 首先添加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"
  1. 修改你的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);
    }
}
  1. 在Dagger Module中绑定这个Factory:
@Module
public abstract class ViewModelModule {
    @Binds
    abstract ViewModelProvider.Factory bindViewModelFactory(MyViewModel.Factory factory);
}
  1. 在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 06:42:06