LiveData setValue should have triggered the onChanged method in the Activity, however it calls only at the first time, after when I try to make paging, it breaks and doesn't call onChanged anymore, though my response is does successful and I see it in the log. What's wrong with setValue/postValue? Is it a bug? Should I implement observer pattern on my own? Whats the point of using LiveData then? My paging doesn't work merely for this already 2-3 days.....
MainActivity class
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();
}
ViewModel
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;
}
Repository
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;
}
The Activity shouldn't have any MutablieLiveData member variables, that should be inside the ViewModel.
The reason for why it only works the first time, is because the first time you observe something it notifes as changed, however because your arrangement is incorrect it never updates again. That is, because ArticleRepository is recreated again inside your ViewModel with a new set of MutableLiveData, the previous one you subscribed to is no longer relevant - and you only subscribe once onCreate()
.
You should separate bind from async-tasks such as loadData()
they are not the same thing. Binding is what you do at the beginning to gather the MutableLiveData (what you are doing in loadData), but after you've done that once you shouldn't do it again.
I also noted that you are actually having LiveData inside the model, it's not recommended to do it this way as it breaks the pattern and can bring other issues. It's the ViewModel that is supposed to prepare the presentation, not the Repository. As you currently have configured things your repository might as well be called the ViewModel. Instead what you should do is use observables to notify the ViewModel of a new batch to post or handle possible errors that occurred.
Study this example: https://developer.android.com/topic/libraries/architecture/viewmodel
Note that loadUsers()
is done once when getUsers()
is called. This is what binds the Activity to the ViewModel. But loadUsers()
can be done again later and should post the changes to the LiveData inside the ViewModel.