Android中Dagger 2字段注入失效求助(MVVM场景)
解决Android MVVM模式下Dagger 2字段注入始终为null的问题
嘿,我来帮你排查这个头疼的问题!结合你用的MVVM模式,Dagger 2注入字段为null大概率是踩了几个常见的坑,咱们一步步来捋清楚:
1. 核心问题:ViewModel没有被Dagger接管创建
你现在用ViewModelProviders.of(this).get(ProfileViewModel.class)创建ViewModel,但Dagger默认不会自动注入ViewModelProviders生成的实例——因为这个实例是Android架构组件自己创建的,Dagger没机会插手初始化里面的@Inject字段。
解决办法是让Dagger来负责ViewModel的创建,也就是自定义一个ViewModelFactory,把ViewModel的实例生成交给Dagger:
第一步:给ViewModel添加@Inject构造函数
把ViewModel里需要注入的依赖通过构造函数传入,并用@Inject标注构造函数,让Dagger知道怎么创建这个ViewModel:
public class ProfileViewModel extends ViewModel { private final UserRepository userRepository; private MutableLiveData<User> userLiveData = new MutableLiveData<>(); // 用@Inject标注构造函数,Dagger会自动传入依赖 @Inject public ProfileViewModel(UserRepository userRepository) { this.userRepository = userRepository; } public void init() { // 这里就可以安全使用userRepository了,不会为null User user = userRepository.getCurrentUser(); userLiveData.postValue(user); } public LiveData<User> getUser() { return userLiveData; } }
第二步:实现Dagger支持的ViewModelFactory
这个Factory会从Dagger的依赖容器中获取ViewModel实例:
@Singleton public class DaggerViewModelFactory implements ViewModelProvider.Factory { private final Map<Class<? extends ViewModel>, Provider<ViewModel>> viewModelMap; @Inject public DaggerViewModelFactory(Map<Class<? extends ViewModel>, Provider<ViewModel>> viewModelMap) { this.viewModelMap = viewModelMap; } @SuppressWarnings("unchecked") @Override public <T extends ViewModel> T create(Class<T> modelClass) { Provider<? extends ViewModel> viewModelProvider = viewModelMap.get(modelClass); // 处理继承的ViewModel情况 if (viewModelProvider == null) { for (Map.Entry<Class<? extends ViewModel>, Provider<ViewModel>> entry : viewModelMap.entrySet()) { if (modelClass.isAssignableFrom(entry.getKey())) { viewModelProvider = entry.getValue(); break; } } } if (viewModelProvider == null) { throw new IllegalArgumentException("Unknown ViewModel class: " + modelClass.getName()); } try { return (T) viewModelProvider.get(); } catch (Exception e) { throw new RuntimeException(e); } } }
第三步:用Dagger模块绑定ViewModel和Factory
创建一个ViewModelModule,把你的ProfileViewModel和Factory绑定到Dagger的依赖图中:
// 自定义注解,用来标记ViewModel的MapKey @MapKey @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface ViewModelKey { Class<? extends ViewModel> value(); } // ViewModel绑定模块 @Module public abstract class ViewModelModule { // 把ProfileViewModel绑定到Dagger的ViewModel映射中 @Binds @IntoMap @ViewModelKey(ProfileViewModel.class) abstract ViewModel bindProfileViewModel(ProfileViewModel viewModel); // 把自定义的Factory绑定到ViewModelProvider.Factory接口 @Binds abstract ViewModelProvider.Factory bindViewModelFactory(DaggerViewModelFactory factory); }
第四步:在Activity中注入Factory并创建ViewModel
别忘了先让Dagger注入Activity本身,然后用注入的Factory创建ViewModel:
public class ProfileActivity extends AppCompatActivity { private static final String TAG = "ProfileActivity"; // 注入Dagger的ViewModelFactory @Inject ViewModelProvider.Factory viewModelFactory; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 关键:先让Dagger注入当前Activity AndroidInjection.inject(this); setContentView(R.layout.activity_profile); // 用Dagger的Factory创建ViewModel ProfileViewModel profileViewModel = ViewModelProviders.of(this, viewModelFactory).get(ProfileViewModel.class); profileViewModel.init(); profileViewModel.getUser().observe(this, user -> { Log.d(TAG, "Received user: " + user.toString()); // 更新UI逻辑 }); } }
2. 其他可能的坑
- 忘记注入Activity/Fragment:如果没调用
AndroidInjection.inject(this),Dagger不会初始化Activity里的@Inject字段(包括viewModelFactory),后续步骤全白搭。 - 依赖未被Dagger管理:比如你的
UserRepository有没有用@Inject构造函数,或者在Dagger模块中提供?如果依赖本身不在Dagger的依赖图里,注入时会直接编译报错(极端情况才会出现运行时为null,Dagger一般会在编译期检查)。 - 混淆导致注入点被破坏:如果开启了混淆,要在proguard规则中添加Dagger和ViewModel的保护规则:
-keep class dagger.** { *; } -keep class javax.inject.** { *; } -keep class * extends androidx.lifecycle.ViewModel { *; } -keepclassmembers class * { @javax.inject.Inject <init>(...); }
总结
最常见的原因就是ViewModel没有通过Dagger的Factory创建,导致Dagger无法初始化ViewModel内部的字段。按照上面的步骤把ViewModel的创建交给Dagger,基本就能解决字段为null的问题啦!
内容的提问来源于stack exchange,提问作者Yamashiro Rion
相关产品推荐
相关产品推荐

