Android ViewModel在屏幕旋转上重新创建



我发现架构组件ViewModel未保留的情况 - 简而言之,它如下:

  1. 活动开始并创建ViewModel实例
  2. 活动被放到后台
  3. 旋转设备屏幕
  4. 活动被放回前景
  5. ViewModel的onCleared方法被调用并创建了新对象

在这种情况下,我的ViewModel实例被销毁是正常的行为吗?如果是这样,是否有建议保持其状态的解决方案?

我能想到的一种方法是在称为onCleared后将其保存,但是,每当活动实际完成时,它也会持续到状态。另一种方法可能是利用onRestoreInstanceState,但在每个屏幕旋转上都触发了(不仅是当应用在后台时(。

任何用于处理这种情况的银弹?

是的@tomwyr,这是来自Android框架的错误。错误详细信息

该修复程序可在28.0.0-alpha3和androidx 1.0.0-alpha3

中使用

但是,如果您现在不想更新到上述版本本身,那么您可以这样解决(我知道这是一个不好的解决方案,但我看不到其他任何好方法(

在您的活动中覆盖 onDestroy方法,然后将所有必需的字段保存到本地变量之前,请在调用Super.ond.ond.ondestroy 之前。现在致电super.ondestroy,然后再次初始化您的ViewModel,然后将所需字段分配回您的新实例

关于Isfinishing

以下代码在Kotlin 中:

override fun onDestroy() {
     val oldViewModel = obtainViewModel()
     if (!isFinishing) { //isFinishing will be false in case of orientation change
          val requiredFieldValue = oldViewModel.getRequiredFieldValue()
          super.onDestroy
         val newViewModel = obtainViewModel()
         if (newViewModel != oldViewModel) { //View Model has been destroyed
              newViewModel.setRequiredFieldValue(requiredFieldValue)
          }
      } else {
         super.onDestroy
      }
 }
private fun obtainViewModel(): SampleViewModel {
      return ViewModelProviders.of(this).get(SampleViewModel::class.java)
}

afaik, ViewModel的唯一目的是在其所有者经历不同的生命周期事件时生存并保留数据(即"保存状态"(。因此,您不必自己"保存状态"。

我们可以从中分辨出是"不是正常行为"。onCleared()仅在活动完成后才调用(并且不会再次重新创建(。

您是使用ViewModelProvider创建ViewModel,还是使用构造函数创建实例?

在您的活动中,您应该有类似的东西:

// in onCreate() - for example - of your activity
model = ViewModelProviders.of(this).get(MyViewModel.class);
// then use it anywhere in the activity like so
model.someAsyncMethod().observe(this, arg -> {
    // do sth...
});

这样做,您应该获得预期效果。

对于其他人可能没有像我这样的答案没有帮助的其他人,问题可能是您没有使用工厂正确设置ViewModelProvider。

挖掘后,我通过将以下方法添加到我的活动中解决了我的类似问题:

protected final <T extends ViewModel> T obtainViewModel(@NonNull AppCompatActivity activity, @NonNull Class<T> modelClass) {
    ViewModelProvider.AndroidViewModelFactory factory = ViewModelProvider.AndroidViewModelFactory.getInstance(activity.getApplication());
    return new ViewModelProvider(activity, factory).get(modelClass);
}

然后我在片段中做到了:

protected final <T extends ViewModel> T obtainFragmentViewModel(@NonNull FragmentActivity fragment, @NonNull Class<T> modelClass) {
    ViewModelProvider.AndroidViewModelFactory factory = ViewModelProvider.AndroidViewModelFactory.getInstance(fragment.getApplication());
    return new ViewModelProvider(fragment, factory).get(modelClass);
}

我已经有一些用于菜单目的的抽象超级课程,因此我将方法隐藏在那里,因此我不必在每个活动中重复它。这就是为什么他们受到保护的原因。我相信,如果您将它们放在需要的所有活动或碎片中,它们可能是私人的。

要尽可能清楚,然后我会在我的活动中调用方法,以分配我的视图模型,看起来像这样的东西

private MyViewModel myViewModel;
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    myViewModel = obtainViewModel(this, MyViewModel.class);
}

或片段

private MyViewModel myViewModel;
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    if (getActivity() != null) {
        myViewModel = obtainFragmentViewModel(getActivity(), MyViewModel.class);
    }
}

更改支持库/compilesdk/targetsdk到28。

我与多窗口有类似的问题。切换到拆分屏幕时,重新创建了我的ViewModel。支持库28解决了我的问题。(我的生命周期版本是1.1.1(

最新更新