如何以不导致数据回流的方式缓存PagingData [英] How to cache PagingData in a way that it does not cause its data to reflow

查看:36
本文介绍了如何以不导致数据回流的方式缓存PagingData的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

我正在使用RoomPaging 3 library,为了填充ConversationFragment,我正在使用PagingLiveData实现和属于ConversationFragmentAndroidViewModel

我在这里使用的不是Navigation Components,根据Android文档,这只是一个通用的片段导航。

我可以从该片段中打开DetailsFragment,然后再次返回到该片段。一切正常,直到我打开所述片段并返回,那么绑定在ConversationFragment中的观察者就会丢失,因为该片段在打开DetailsFragment时会被销毁。

到目前为止,这不是一个大问题,我可以再次重新启动观察器,当我这样做时,它确实可以工作。 但是,当我再次附加观察器时,整个列表回流,这会导致RecyclerView中的项目变得狂野,列表所在的位置丢失,并且滚动条更改大小以确认页面正在加载/重新加载。

我可以在一定程度上忍受这种怪异的行为,但在此基础上失去职位是不可接受的。

我研究了在视图模型中缓存结果,但我可以在可用的文档中找到的示例都是基本的,没有说明如何使用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;

    }

}

实际上,即使我返回PagingLiveData.cachedIn的结果,当我返回到片段时,行为也是一样的;项目在回收视图列表中显示不稳定的行为,并且它所在的位置完全丢失。

这就是我试图实现的目标,看看它是否解决了我的问题:

这是此处提供的代码实验室:https://developer.android.com/codelabs/android-training-livedata-viewmodel#8

如您所见,mAllWords被缓存,它们仅在第一次构造视图模型时初始化,任何后续更改都只是更新,并且只需要在片段被销毁并在后台堆栈中重新创建时附加新的观察器。

这就是我想要做的,但它并不像我想象的那样工作,至少不像我想象的那么简单。

如何实现此目标?

推荐答案

这里有很多需要解包的内容,但我最好的猜测是ConversationViewModel中的getList方法。使用ViewModels和LiveData跨导航持久化数据是正确的,但是在这里,您是在每次调用此方法时重新创建LiveData,这意味着当您恢复ConversationFragmentonViewCreated被调用时,它会创建一个新的分页程序来获取新数据。

解决方案是在第一次创建ConversationViewModel时创建分页向导,然后访问LiveData对象本身,而不是方法。您可以在Codelab示例中看到这一点,他们在构造函数中分配LiveData,并在getAllWords()方法中简单地返回已经创建的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(),只是这次它返回的是预先存在的版本。

viewModel.getList().observe(getViewLifecycleOwner(), pagingData -> 
    adapter.submitData(lifecycleOwner.getLifecycle(), pagingData));
}

我无法测试这是否可以编译或工作,如果不能,请让我知道,我将更新此答案。

这篇关于如何以不导致数据回流的方式缓存PagingData的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆