Android屏幕旋转时MainViewModel重复创建问题咨询
看起来你遇到了ViewModel在配置变化时重复创建的问题,我来帮你分析一下原因和对应的解决方案:
问题原因分析
从你的代码和日志来看,每次旋转手机触发配置变化时MainViewModel的构造函数都会被调用,这和官方文档中ViewModel会在配置变化时保留实例的描述不符,问题大概率出在这几个地方:
1. ViewModelFactory的实现存在问题
你的MainViewModel用@Inject标记构造函数,说明依赖Dagger创建实例。如果你的ViewModelFactory实现逻辑有问题——比如每次调用create()方法都会返回新的MainViewModel,或者没有正确复用Dagger提供的作用域实例——就会导致ViewModelProviders.get()每次都创建新对象。
2. BaseActivity的ViewModel赋值逻辑有漏洞
在BaseActivity的performDataBinding()方法里:
this.viewModel = viewModel == null ? onCreateViewModel() : viewModel;
当Activity旋转重建时,成员变量viewModel会被重置为null,所以每次都会触发onCreateViewModel()。虽然ViewModelProviders.of(this).get()理论上应该返回已有的实例,但如果ViewModelStore中的实例被意外清空,就会触发Factory创建新实例。
3. ViewModelStoreOwner不正确
如果你的BaseActivity直接继承自原生Activity,而不是AppCompatActivity或FragmentActivity,它就没有实现ViewModelStoreOwner接口,ViewModelProviders.of(this)无法获取正确的ViewModelStore,自然无法保留旧实例。
4. Lifecycle依赖库版本过低
旧版本的Android Lifecycle库可能存在配置变化时ViewModel无法保留的bug,这也会导致重复创建的问题。
解决方案
步骤1:检查并修复ViewModelFactory实现
确保你的ViewModelFactory正确实现ViewModelProvider.Factory,并且通过Dagger作用域复用ViewModel实例。标准的Dagger ViewModelFactory实现应该是这样的:
@Singleton public class ViewModelFactory implements ViewModelProvider.Factory { private final Map<Class<? extends ViewModel>, Provider<ViewModel>> creators; @Inject public ViewModelFactory(Map<Class<? extends ViewModel>, Provider<ViewModel>> creators) { this.creators = creators; } @SuppressWarnings("unchecked") @Override public <T extends ViewModel> T create(Class<T> modelClass) { Provider<? extends ViewModel> creator = creators.get(modelClass); if (creator == null) { for (Map.Entry<Class<? extends ViewModel>, Provider<ViewModel>> entry : creators.entrySet()) { if (modelClass.isAssignableFrom(entry.getKey())) { creator = entry.getValue(); break; } } } if (creator == null) { throw new IllegalArgumentException("Unknown ViewModel class: " + modelClass); } try { return (T) creator.get(); } catch (Exception e) { throw new RuntimeException(e); } } }
同时给MainViewModel添加@ActivityScope(或对应作用域),确保Dagger在Activity生命周期内复用该实例。
步骤2:修改BaseActivity的ViewModel获取逻辑
去掉成员变量的null检查,直接调用onCreateViewModel()——因为ViewModelProviders.get()本身会自动复用已有的实例,无需手动判断:
private void performDataBinding() { viewDataBinding = DataBindingUtil.setContentView(this, setLayoutId()); // 直接获取ViewModel,ViewModelProviders会自动返回已有的实例 this.viewModel = onCreateViewModel(); viewDataBinding.setVariable(setBindingVariable(), viewModel); viewDataBinding.executePendingBindings(); }
步骤3:确认BaseActivity的父类
确保BaseActivity继承自AppCompatActivity或FragmentActivity,这些类已经实现了ViewModelStoreOwner接口,能正确管理ViewModelStore:
public abstract class BaseActivity extends AppCompatActivity { // ... 你的其他代码 }
步骤4:升级Lifecycle依赖库
在build.gradle中升级到最新的Lifecycle稳定版本(比如2.6.1):
dependencies { implementation "androidx.lifecycle:lifecycle-viewmodel:2.6.1" implementation "androidx.lifecycle:lifecycle-livedata:2.6.1" implementation "androidx.lifecycle:lifecycle-runtime:2.6.1" }
步骤5:排查ViewModelStore是否被意外清空
检查你的代码中是否有调用ViewModelStore.clear()的逻辑(比如在onDestroy()方法中),这会直接清空ViewModel实例,导致每次重建都要创建新对象。
按照以上步骤排查后,应该就能解决旋转手机时ViewModel重复创建的问题了。
内容的提问来源于stack exchange,提问作者Ivan Sablin

