Android 带有 SavedState 的 ViewModel 重写了新实例的参数
Android ViewModel with SavedState rewrites arguments for new instances
我有一个具有 Master/Detail 类型架构的应用程序。当我 select 某些项目时,详细信息显示在详细信息片段中。
要了解要加载什么的详细片段 id
是通过 Arguments
发送的。在 VM 中使用 SavedStateHandle
我可以直接从句柄中读取这些参数,而无需在 Fragment
本身中重新路由它。这对第一个细节片段非常有效。问题来自下一个 selections.
每次我加载详细信息时,第一个 selection 的 id
都会被填充,尽管每次都会创建 新片段 以及 新视图模型.
我正在查看 lifecycle-viewmodel-savedstate 库的代码 (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
.
中的正确密钥
我可以理解这可能是真正恢复所需的行为,但在我的情况下我没有恢复片段。是的,class 是相同的,但我只是将细节片段替换为另一个包含 new/different 数据的细节片段。
我做错了什么吗?我可以向框架提示这不是恢复并且我对旧片段中保存的值不感兴趣吗?
我没有提到的是我正在使用 ViewPager2
进行 Master/Detail 视图。我不认为它对状态停止有一些影响,但它有。
ViewPager2
库正在后台保存和恢复状态,然后由 SavedStateHandle
使用。要使用状态映射片段,它使用来自 FragmentStateAdapter
的 ID。我没有重写 getItemId
方法,所以 ID 是按位置分配的。
这当然会将相同的状态映射到确切位置上的新片段。实施 getItemId
为不同的片段实例分配不同的 ID 解决了这个问题。
我有一个具有 Master/Detail 类型架构的应用程序。当我 select 某些项目时,详细信息显示在详细信息片段中。
要了解要加载什么的详细片段 id
是通过 Arguments
发送的。在 VM 中使用 SavedStateHandle
我可以直接从句柄中读取这些参数,而无需在 Fragment
本身中重新路由它。这对第一个细节片段非常有效。问题来自下一个 selections.
每次我加载详细信息时,第一个 selection 的 id
都会被填充,尽管每次都会创建 新片段 以及 新视图模型.
我正在查看 lifecycle-viewmodel-savedstate 库的代码 (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
.
我可以理解这可能是真正恢复所需的行为,但在我的情况下我没有恢复片段。是的,class 是相同的,但我只是将细节片段替换为另一个包含 new/different 数据的细节片段。
我做错了什么吗?我可以向框架提示这不是恢复并且我对旧片段中保存的值不感兴趣吗?
我没有提到的是我正在使用 ViewPager2
进行 Master/Detail 视图。我不认为它对状态停止有一些影响,但它有。
ViewPager2
库正在后台保存和恢复状态,然后由 SavedStateHandle
使用。要使用状态映射片段,它使用来自 FragmentStateAdapter
的 ID。我没有重写 getItemId
方法,所以 ID 是按位置分配的。
这当然会将相同的状态映射到确切位置上的新片段。实施 getItemId
为不同的片段实例分配不同的 ID 解决了这个问题。