如何以不会导致其数据回流的方式缓存 PagingData

How to cache PagingData in a way that it does not cause its data to reflow

我有 2 个片段的简单设置:ConversationFragmentDetailsFragment

我正在使用 RoomPaging 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,这意味着当您恢复 ConversationFragmentonViewCreated 被调用时,它创建一个新的寻呼机来获取新数据。

解决方案是在首次创建 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));
}

我无法测试它是否可以编译或工作,如果不能,请告诉我,我会更新这个答案。