【问题标题】:Android ViewModel with SavedState rewrites arguments for new instances带有 SavedState 的 Android ViewModel 重写新实例的参数
【发布时间】:2021-07-11 20:30:40
【问题描述】:

我有一个应用程序,它具有 Master/Detail 类型的架构。当我选择某个项目时,详细信息会显示在详细信息片段中。

要知道要加载什么的详细信息片段id 是通过Arguments 发送的。在 VM 中使用 SavedStateHandle 我可以直接从句柄中读取这些参数,而无需在 Fragment 本身中重新路由它。这对于第一个细节片段非常有用。问题出现在下一个选择中。

每次我加载详细信息时,都会填充第一次选择的id,尽管每次创建新片段以及新视图模型

我正在查看生命周期视图模型保存状态库的代码 (v.2.3.1) 并在 SavedStateHandle 中找到了这个方法调用:

static SavedStateHandle createHandle(@Nullable Bundle restoredState,
        @Nullable Bundle defaultState) {
    if (restoredState == null && defaultState == null) {
        return new SavedStateHandle();
    }

    Map<String, Object> state = new HashMap<>();
    if (defaultState != null) {
        for (String key : defaultState.keySet()) {
            state.put(key, defaultState.get(key));
        }
    }

    if (restoredState == null) {
        return new SavedStateHandle(state);
    }

    ArrayList keys = restoredState.getParcelableArrayList(KEYS);
    ArrayList values = restoredState.getParcelableArrayList(VALUES);
    if (keys == null || values == null || keys.size() != values.size()) {
        throw new IllegalStateException("Invalid bundle passed as restored state");
    }
    for (int i = 0; i < keys.size(); i++) {
        state.put((String) keys.get(i), values.get(i));
    }
    return new SavedStateHandle(state);
}

在这里我可以看到defaultState 为每个新视图模型设置了正确的 id。但正如您所见,defaultState 首先处理,restoredState 然后处理。 restoredState 包含 与旧的 id 相同的密钥,最后替换来自 defaultState 的正确密钥。

我可以理解这可能是真正恢复所需的行为,但在我的情况下,我没有恢复片段。是的,类是相同的,但我只是用另一个具有新/不同数据的细节片段替换细节片段。

我做错了吗?我可以给框架一个提示,这不是恢复并且我对旧片段中保存的值不感兴趣吗?

【问题讨论】:

  • 如果您正在创建一个全新的片段并因此获得一个新的 ViewModel,那么 restoredState 将为空。是什么让您认为它不为空?
  • 调试?我可以看到它不为空。根据定义, saveInstanceState 应该在实例之间保存状态,所以我并不感到惊讶。但在这种情况下,这并不是真正需要的行为。
  • 一个全新的 Fragment 和一个 ViewModel 关联的工作一个全新的 Fragment 没有保存状态。请包括您如何创建全新的 Fragment 以及如何创建 ViewModel
  • 确定是什么原因造成的!感谢您指出 Fragment 创建,深入研究我找到了罪魁祸首。查看答案。

标签: android android-fragments android-lifecycle android-savedstate viewmodel-savedstate


【解决方案1】:

我没有提到的是我正在使用ViewPager2 进行主/详细视图。我不认为它对 state staving 有一些影响,但确实有。

ViewPager2 库正在后台保存和恢复状态,然后由SavedStateHandle 使用。为了映射带有状态的片段,它使用来自FragmentStateAdapter 的 id。我没有覆盖 getItemId 方法,所以 id 是按位置分配的。

这当然会将相同的状态映射到确切位置上的新片段。实现 getItemId 将不同的 id 分配给片段的不同实例解决了这个问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-04-02
    • 2020-04-30
    • 2020-03-27
    • 2016-01-08
    • 1970-01-01
    • 2020-07-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多