10

我发现ViewModel没有保留架构组件的情况 - 简而言之,如下所示:

  1. Activity 启动并ViewModel创建实例
  2. 活动被置于后台
  3. 设备屏幕旋转
  4. 活动被放回前台
  5. 调用ViewModel 的onCleared方法并创建新对象

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

我能想到的一种方法onCleared是调用一次保存它,但是,只要活动实际完成,它也会保持状态。另一种方法可能是使用,onRestoreInstanceState但它会在每次屏幕旋转时触发(不仅在应用程序处于后台时)。

有什么灵丹妙药可以处理这种情况吗?

4

4 回答 4

9

是的@tomwyr,这是来自 android 框架的错误。错误详情

该修复适用于 28.0.0-alpha3 和 AndroidX 1.0.0-alpha3

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

在您的活动中覆盖onDestroy 方法并在调用 super.onDestroy 之前将所有必需的字段保存到局部变量。现在调用 super.onDestroy 然后再次初始化您的 ViewModel 并将必填字段分配回您的新 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)
}
于 2018-07-23T06:46:27.680 回答
1

AFAIK,ViewModel的唯一目的是在其所有者经历不同的生命周期事件时生存并保留数据(即“保存状态”)。所以你不必自己“拯救国家”。

我们可以从中看出这是“不正常的行为”。onCleared()仅在活动完成后调用(并且不会再次重新创建)。

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

在您的活动中,您应该具有以下内容:

// 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...
});

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

于 2018-04-24T14:30:52.183 回答
1

对于像我这样的以前的答案可能无法帮助的其他人,问题可能是您没有使用工厂正确设置 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);
}

我已经有一些用于菜单目的的抽象超类,所以我将方法隐藏在那里,这样我就不必在每个活动中重复它。这就是他们受到保护的原因。我相信如果你把它们放在你需要它们的每一个活动或片段中,它们可能是私有的。

为了尽可能清楚,然后我会调用方法在我的活动中的 onCreate() 中分配我的视图模型,它看起来像这样

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);
    }
}
于 2020-06-14T20:22:01.260 回答
0

将支持库/compileSDK/targetSDK 更改为 28。

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

于 2018-12-13T19:54:24.743 回答