首页 > 解决方案 > 为什么 LiveData setValue 或 PostValue 在视图中只触发一次 onChange?

问题描述

LiveData setValue 应该触发了 Activity 中的 onChanged 方法,但是它只在第一次调用,当我尝试进行分页之后,它会中断并且不再调用 onChanged,尽管我的响应确实成功并且我看到它在日志。setValue/postValue 有什么问题?它是一个错误吗?我应该自己实现观察者模式吗?那么使用 LiveData 有什么意义呢?我的寻呼仅在这 2-3 天后不起作用.....

  1. MainActivity 类

     public class MainActivity extends AppCompatActivity 
     private MutableLiveData<List<Photo>> mLivePhotos;
     // some code...
    
     @Override
     protected void onCreate(Bundle savedInstanceState) {
        mLivePhotos = loadData();
        mLivePhotos.observe(this, photos -> {
            Log.d(TAG, "onChanged!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
            mProgressBar.setVisibility(View.GONE);
            mPhotos = photos;
            if (mIsInitialCall) {
                initiateAdapter();
                mIsInitialCall = false;
            } else {
                mAdapter.updateList(mPhotos.subList(mPageNumber, mPageNumber + 10));
            }
        });
    
        mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                int lastPosition = 
    mLayoutManager.findLastCompletelyVisibleItemPosition();
                Log.d(TAG, "onScrolled - lastPosition: " + lastPosition);
                if (lastPosition == mLayoutManager.getItemCount() - 1) {
                    Log.d(TAG, "onScrolled - End of list?");
                    loadData();
                }
            }
        });
    }
    
    private MutableLiveData<List<Photo>> loadData() {
        Log.d(TAG, "loadData");
        if (mArticleViewModel == null) return null;
        mPageNumber += 10;
        mProgressBar.setVisibility(View.VISIBLE);
        return mArticleViewModel.loadPhotos();
    }
    
  2. 视图模型

    public class ArticleViewModel extends ViewModel {
        private MutableLiveData<List<Photo>> photos;
        private ArticleRepository articleRepository;
    
        public MutableLiveData<List<Photo>> loadPhotos() {
            Log.d(TAG, "getArticleList");
            //TODO; add Dagger 2
            articleRepository = new ArticleRepository();
            photos = articleRepository.getPhotos();
            return photos;
        }
    
  3. 存储库

    public class ArticleRepository {
    
        public MutableLiveData<List<Photo>> getPhotos() {
            final MutableLiveData<List<Photo>> result = new MutableLiveData<>();
            Log.d(TAG, "getResults");
            ApiService.getService().getPhotos().enqueue(new Callback<List<Photo>>() {
            @Override
            public void onResponse(Call<List<Photo>> call, Response<List<Photo>> response) {
                Log.d(TAG, "onResponse");
                if (response.isSuccessful()) {
                    Log.d(TAG, "isSuccessful");
                    result.postValue(response.body());
                }
            }
    
            @Override
            public void onFailure(Call<List<Photo>> call, Throwable t) {
                Log.d(TAG, "onFailure: " + t.getMessage() + "\n" +  t.getStackTrace());
            }
        });
        return result;
    }
    

标签: javaandroidkotlinandroid-livedataandroid-paging

解决方案


Activity 不应该有任何 MutablieLiveData 成员变量,它应该在 ViewModel 中。

它只在第一次工作的原因是因为你第一次观察到它通知的东西已经改变,但是因为你的安排不正确,它永远不会再更新。也就是说,因为 ArticleRepository 在您的 ViewModel 中再次使用一组新的 MutableLiveData 重新创建,所以您订阅的前一个不再相关 - 您只订阅一次onCreate()

您应该将绑定与异步任务分开,因为loadData()它们不是同一件事。绑定是您在开始收集 MutableLiveData(您在 loadData 中所做的事情)时所做的事情,但在您完成之后,您不应该再做一次。

我还注意到您实际上在模型中有 LiveData,不建议这样做,因为它会破坏模式并可能带来其他问题。准备演示文稿的是 ViewModel,而不是 Repository。由于您当前已经配置了一些东西,您的存储库也可以称为 ViewModel。相反,您应该做的是使用 observables 通知 ViewModel 有一个新批次来发布或处理可能发生的错误。

研究这个例子:https ://developer.android.com/topic/libraries/architecture/viewmodel

请注意,调用loadUsers()getUsers()会执行一次。这就是将 Activity 绑定到 ViewModel 的原因。但loadUsers()可以稍后再完成,并且应该将更改发布到 ViewModel 内的 LiveData。


推荐阅读