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

如何用Dagger2在App作用域ViewModel中注入依赖Activity的Navigator

解决App作用域ViewModel注入依赖Activity的Navigator问题

看起来你遇到了Dagger作用域不匹配的典型问题:你的HomeViewModel绑定在**@Singleton**的VMAppComponent中(属于App全局作用域),但HomeNavigator依赖于Activity(属于Activity作用域),直接构造注入会导致Dagger报错,同时还可能引发内存泄漏风险(App作用域的ViewModel持有Activity引用,Activity销毁后无法被回收)。

下面给你两个最合理的解决方案:


方案一:将ViewModel移至Activity作用域(推荐)

既然HomeViewModel是和HomeActivity强关联的,它根本不需要放在App全局作用域。我们可以把ViewModel的绑定移到Activity级别的Component中,这样就能直接注入依赖Activity的HomeNavigator了。

步骤1:定义Activity作用域

首先创建一个Activity级别的作用域注解:

@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface ActivityScope {}

步骤2:调整ViewModel绑定到Activity模块

创建HomeActivityModule,将HomeViewModel的绑定和HomeNavigator的实现绑定放在这里:

@Module
public abstract class HomeActivityModule {
    // 绑定HomeViewModel到ViewModel映射
    @Binds
    @IntoMap
    @ViewModelKey(HomeViewModel.class)
    abstract ViewModel bindHomeViewModel(HomeViewModel homeViewModel);

    // 绑定HomeNavigator的实现(HomeActivity本身实现了HomeNavigator)
    @Binds
    abstract HomeNavigator bindHomeNavigator(HomeActivity homeActivity);
}

步骤3:修改HomeActivityComponent

让HomeActivityComponent依赖VMAppComponent(获取App级别的依赖如BaseSchedulerProvider),并标记为@ActivityScope:

@ActivityScope
@Component(dependencies = VMAppComponent.class, modules = {BaseActivityModule.class, HomeActivityModule.class})
public interface HomeActivityComponent {
    void inject(HomeActivity homeActivity);
}

步骤4:修改HomeViewModel的构造注入

现在可以直接在构造函数中注入HomeNavigator了:

public class HomeViewModel extends ViewModel {
    @NonNull
    private final HomeNavigator navigationProvider;
    @NonNull
    private final BaseSchedulerProvider schedulerProvider;

    @Inject
    public HomeViewModel(
            @NonNull BaseSchedulerProvider schedulerProvider,
            @NonNull HomeNavigator navigationProvider
    ) {
        this.schedulerProvider = schedulerProvider;
        this.navigationProvider = navigationProvider;
    }

    void handleAddButtonClick() {
        navigationProvider.addNewVehicle();
    }
}

步骤5:在HomeActivity中初始化Component和ViewModel

public class HomeActivity extends AppCompatActivity implements HomeNavigator {
    @Inject
    ViewModelProvider.Factory viewModelFactory;
    private HomeViewModel viewModel;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 初始化Activity Component
        HomeActivityComponent component = ((VMApp) getApplication())
                .getAppComponent()
                .injectHomeActivity(new BaseActivityModule(this));
        component.inject(this);

        // 通过Dagger的Factory获取ViewModel
        viewModel = new ViewModelProvider(this, viewModelFactory).get(HomeViewModel.class);
    }

    @Override
    public void addNewVehicle() {
        // 实现跳转逻辑,比如启动AddEditVehicleActivity
        startActivity(new Intent(this, AddEditVehicleActivity.class));
    }
}

方案二:使用Assisted Injection(适合跨作用域参数的场景)

如果你的ViewModel确实需要保留在App作用域,同时要注入Activity级别的依赖,可以用Dagger的Assisted Inject来处理混合作用域的构造参数。

步骤1:修改HomeViewModel使用@AssistedInject

public class HomeViewModel extends ViewModel {
    @NonNull
    private final HomeNavigator navigationProvider;
    @NonNull
    private final BaseSchedulerProvider schedulerProvider;

    // 用@AssistedInject标记构造函数,Activity级别的参数用@Assisted标记
    @AssistedInject
    public HomeViewModel(
            @NonNull BaseSchedulerProvider schedulerProvider,
            @Assisted @NonNull HomeNavigator navigationProvider
    ) {
        this.schedulerProvider = schedulerProvider;
        this.navigationProvider = navigationProvider;
    }

    // 定义Assisted Factory,用于创建ViewModel时传入Activity级参数
    @AssistedFactory
    public interface Factory {
        HomeViewModel create(HomeNavigator navigationProvider);
    }

    void handleAddButtonClick() {
        navigationProvider.addNewVehicle();
    }
}

步骤2:调整ViewModelFactory适配AssistedInject

你需要修改ViewModelFactory来支持Assisted Factory,或者使用Dagger提供的AssistedViewModelFactory(需要添加对应的依赖)。

步骤3:在Activity中创建ViewModel并传入Navigator

public class HomeActivity extends AppCompatActivity implements HomeNavigator {
    @Inject
    HomeViewModel.Factory viewModelFactory;
    private HomeViewModel viewModel;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 初始化Component
        ((VMApp) getApplication()).getAppComponent().injectHomeActivity(new BaseActivityModule(this)).inject(this);

        // 通过Assisted Factory创建ViewModel,传入当前Activity作为Navigator
        viewModel = new ViewModelProvider(this, new ViewModelProvider.Factory() {
            @NonNull
            @Override
            public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
                if (modelClass.isAssignableFrom(HomeViewModel.class)) {
                    return (T) viewModelFactory.create(HomeActivity.this);
                }
                throw new IllegalArgumentException("Unknown ViewModel class");
            }
        }).get(HomeViewModel.class);
    }
}

为什么原方案的Setter注入有风险?

你当前用setNavigationProvider的方式虽然能工作,但存在两个问题:

  1. 空指针风险:如果忘记调用setter,handleAddButtonClick会抛出NPE;
  2. 内存泄漏风险:如果ViewModel是App作用域的,它会持有Activity的引用,当Activity销毁后,ViewModel仍存在于App内存中,导致Activity无法被GC回收。

所以更推荐方案一,让ViewModel的作用域和它关联的Activity保持一致,既符合Dagger的作用域规则,也能避免内存泄漏。

内容的提问来源于stack exchange,提问作者reinaldo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 07:50:32