如何以不会导致其数据回流的方式缓存 PagingData
How to cache PagingData in a way that it does not cause its data to reflow
我有 2 个片段的简单设置:ConversationFragment
和 DetailsFragment
我正在使用 Room
和 Paging 3 library
并填充 ConversationFragment
我正在使用 PagingLiveData
实现以及属于 ConversationFragment
.
我在这里没有使用 Navigation Components
,只是按照 Android 文档的通用片段导航。
从那个片段我可以打开 DetailsFragment
然后 return 再次回到片段。一切正常,直到我打开所述片段和 return,然后绑定在 ConversationFragment
中的观察者丢失了,因为在打开 DetailsFragment
.[=32 时该片段被破坏了=]
到目前为止这不是什么大问题,我可以再次重新启动观察器并且当我这样做时它确实有效。
然而,当我再次附加观察者时,整个列表回流,这导致 RecyclerView
中的项目变得疯狂,列表所在的位置丢失并且滚动条改变大小确认页面正在 loaded/reloaded.
我能在一定程度上忍受这种奇怪的行为,但在此之上失去职位是不可接受的。
我研究了在视图模型中缓存结果,但我在可用文档中找到的示例是基本的,没有说明如何使用 LiveData<PagingData<...>
对象实现相同的效果。
目前我拥有的是:
ConversationFragment
@Override
public void onViewCreated(
@NonNull View view,
@Nullable Bundle savedInstanceState
) {
if (viewModel == null) {
viewModel = new ViewModelProvider(this).get(ConversationViewModel.class);
}
if (savedInstanceState == null) {
// adapter is initialized in onCreateView
viewModel
.getList(getViewLifecycleOwner())
.observe(getViewLifecycleOwner(), pagingData -> adapter.submitData(lifecycleOwner.getLifecycle(), pagingData));
}
super.onViewCreated(view, savedInstanceState);
}
ConversationViewModel
public class ConversationViewModel extends AndroidViewModel {
final PagingConfig pagingConfig = new PagingConfig(10, 10, false, 20);
private final Repository repository;
private final MutableLiveData<PagingData<ItemView>> messageList;
public ConversationFragmentVM(@NonNull Application application) {
super(application);
messageList = new MutableLiveData<>();
repository = new Repository(application);
}
public LiveData<PagingData<ItemView>> getList(@NonNull LifecycleOwner lifecycleOwner) {
// at first I tried only setting the value if it was null
// but since the observer is lost on destroy and the value
// is not it would never be able to "restart" the observer
// again
// if (messageList.getValue() == null) {
PagingLiveData.cachedIn(
PagingLiveData.getLiveData(new Pager<>(pagingConfig, () -> repository.getMessageList())),
lifecycleOwner.getLifecycle()
).observe(lifecycleOwner, messageList::setValue);
// }
return messageList;
}
}
实际上,即使我 return PagingLiveData.cachedIn
的结果与我 return 片段的行为相同;这些项目在 recyclerview 列表中显示不稳定的行为,并且它所在的位置完全丢失。
这是我试图实现的目标,看看它是否解决了我的问题:
这是一个可用的代码实验室:https://developer.android.com/codelabs/android-training-livedata-viewmodel#8
如您所见,mAllWords
已被缓存,并且仅在首次构建视图模型时才初始化,任何后续更改都只是更新,并且只需要在片段被销毁并再次创建,同时仍在后台堆栈中。
这就是我想要做的,但它并没有像我想象的那样工作,至少它不像我想象的那么简单。
如何实现?
这里有很多东西需要解压缩,但我最好的猜测是 ConversationViewModel
中的 getList
方法。使用 ViewModels 和 LiveData 在导航中保存数据是正确的,但在这里每次调用此方法时都会重新创建 LiveData,这意味着当您恢复 ConversationFragment
和 onViewCreated
被调用时,它创建一个新的寻呼机来获取新数据。
解决方案是在首次创建 ConversationViewModel
时创建寻呼机,然后访问 LiveData 对象本身,而不是方法。您可以在 Codelab 示例中看到这一点,他们在构造函数中分配 LiveData,并在 getAllWords()
方法中简单地 return 已创建的 LiveData。
我以 this 为例,将 ConversationViewModel
更改为类似这样的内容并将其更改为使用您的配置和存储库。
private final LiveData<PagingData<ItemView>> messageList;
public ConversationFragmentVM(@NonNull Application application) {
super(application);
repository = new Repository(application);
// CoroutineScope helper provided by the lifecycle-viewmodel-ktx artifact.
CoroutineScope viewModelScope = ViewModelKt.getViewModelScope(viewModel);
Pager<Integer, User> pager = Pager<>(
new PagingConfig(/* pageSize = */ 20),
() -> ExamplePagingSource(backend, query));
messageList = PagingLiveData.cachedIn(PagingLiveData.getLiveData(pager), viewModelScope);
}
public LiveData<PagingData<ItemView>> getList(){
return messageList;
}
然后在您的片段中,您只需像往常一样观察 getList(),只是这次 return 是一个预先存在的版本。
viewModel.getList().observe(getViewLifecycleOwner(), pagingData ->
adapter.submitData(lifecycleOwner.getLifecycle(), pagingData));
}
我无法测试它是否可以编译或工作,如果不能,请告诉我,我会更新这个答案。
我有 2 个片段的简单设置:ConversationFragment
和 DetailsFragment
我正在使用 Room
和 Paging 3 library
并填充 ConversationFragment
我正在使用 PagingLiveData
实现以及属于 ConversationFragment
.
我在这里没有使用 Navigation Components
,只是按照 Android 文档的通用片段导航。
从那个片段我可以打开 DetailsFragment
然后 return 再次回到片段。一切正常,直到我打开所述片段和 return,然后绑定在 ConversationFragment
中的观察者丢失了,因为在打开 DetailsFragment
.[=32 时该片段被破坏了=]
到目前为止这不是什么大问题,我可以再次重新启动观察器并且当我这样做时它确实有效。
然而,当我再次附加观察者时,整个列表回流,这导致 RecyclerView
中的项目变得疯狂,列表所在的位置丢失并且滚动条改变大小确认页面正在 loaded/reloaded.
我能在一定程度上忍受这种奇怪的行为,但在此之上失去职位是不可接受的。
我研究了在视图模型中缓存结果,但我在可用文档中找到的示例是基本的,没有说明如何使用 LiveData<PagingData<...>
对象实现相同的效果。
目前我拥有的是:
ConversationFragment
@Override
public void onViewCreated(
@NonNull View view,
@Nullable Bundle savedInstanceState
) {
if (viewModel == null) {
viewModel = new ViewModelProvider(this).get(ConversationViewModel.class);
}
if (savedInstanceState == null) {
// adapter is initialized in onCreateView
viewModel
.getList(getViewLifecycleOwner())
.observe(getViewLifecycleOwner(), pagingData -> adapter.submitData(lifecycleOwner.getLifecycle(), pagingData));
}
super.onViewCreated(view, savedInstanceState);
}
ConversationViewModel
public class ConversationViewModel extends AndroidViewModel {
final PagingConfig pagingConfig = new PagingConfig(10, 10, false, 20);
private final Repository repository;
private final MutableLiveData<PagingData<ItemView>> messageList;
public ConversationFragmentVM(@NonNull Application application) {
super(application);
messageList = new MutableLiveData<>();
repository = new Repository(application);
}
public LiveData<PagingData<ItemView>> getList(@NonNull LifecycleOwner lifecycleOwner) {
// at first I tried only setting the value if it was null
// but since the observer is lost on destroy and the value
// is not it would never be able to "restart" the observer
// again
// if (messageList.getValue() == null) {
PagingLiveData.cachedIn(
PagingLiveData.getLiveData(new Pager<>(pagingConfig, () -> repository.getMessageList())),
lifecycleOwner.getLifecycle()
).observe(lifecycleOwner, messageList::setValue);
// }
return messageList;
}
}
实际上,即使我 return PagingLiveData.cachedIn
的结果与我 return 片段的行为相同;这些项目在 recyclerview 列表中显示不稳定的行为,并且它所在的位置完全丢失。
这是我试图实现的目标,看看它是否解决了我的问题:
这是一个可用的代码实验室:https://developer.android.com/codelabs/android-training-livedata-viewmodel#8
如您所见,mAllWords
已被缓存,并且仅在首次构建视图模型时才初始化,任何后续更改都只是更新,并且只需要在片段被销毁并再次创建,同时仍在后台堆栈中。
这就是我想要做的,但它并没有像我想象的那样工作,至少它不像我想象的那么简单。
如何实现?
这里有很多东西需要解压缩,但我最好的猜测是 ConversationViewModel
中的 getList
方法。使用 ViewModels 和 LiveData 在导航中保存数据是正确的,但在这里每次调用此方法时都会重新创建 LiveData,这意味着当您恢复 ConversationFragment
和 onViewCreated
被调用时,它创建一个新的寻呼机来获取新数据。
解决方案是在首次创建 ConversationViewModel
时创建寻呼机,然后访问 LiveData 对象本身,而不是方法。您可以在 Codelab 示例中看到这一点,他们在构造函数中分配 LiveData,并在 getAllWords()
方法中简单地 return 已创建的 LiveData。
我以 this 为例,将 ConversationViewModel
更改为类似这样的内容并将其更改为使用您的配置和存储库。
private final LiveData<PagingData<ItemView>> messageList;
public ConversationFragmentVM(@NonNull Application application) {
super(application);
repository = new Repository(application);
// CoroutineScope helper provided by the lifecycle-viewmodel-ktx artifact.
CoroutineScope viewModelScope = ViewModelKt.getViewModelScope(viewModel);
Pager<Integer, User> pager = Pager<>(
new PagingConfig(/* pageSize = */ 20),
() -> ExamplePagingSource(backend, query));
messageList = PagingLiveData.cachedIn(PagingLiveData.getLiveData(pager), viewModelScope);
}
public LiveData<PagingData<ItemView>> getList(){
return messageList;
}
然后在您的片段中,您只需像往常一样观察 getList(),只是这次 return 是一个预先存在的版本。
viewModel.getList().observe(getViewLifecycleOwner(), pagingData ->
adapter.submitData(lifecycleOwner.getLifecycle(), pagingData));
}
我无法测试它是否可以编译或工作,如果不能,请告诉我,我会更新这个答案。