Reputation: 867
I'm developing an app where I need to do a network call every 30 seconds, and delete the previous data and insert the new one. And every time the new data is inserted I'm showing it in the RecyclerView. I'm using Handler to give a network call and LiveData for observing data changes. Everything just works fine, just Live data observer triggers multiple time, so the data is getting deleted and inserted multiple times in result to refresh the RecyclerView frequently causing it to flash multiple times every 30 seconds.
So below is the code what I've tried:
In my Fragment I do this:
private LiveData<List<RestaurantTablesModel>> mData;
private Observer<List<RestaurantTablesModel>> mObserver;
private TablesViewModel mViewModel;
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View mView = inflater.inflate(R.layout.fragment_tables, container, false);
ButterKnife.bind(this, mView);
TablesViewModelFactory factory = InjectorUtils.provideTablesFactory(getActivity());
mViewModel = ViewModelProviders.of(this, factory).get(TablesViewModel.class);
setUpUserRecyclerView();
return mView;
}
private void setUpRecyclerView() {
mData = mViewModel.getTablesData(mLocationID);
mObserver = tablesModels -> {
if (tablesModels != null) {
mTablesRecyclerAdapter.addTables(tablesModels);
Log.e(LOG_TAG, "setUpUserRecyclerView: tablesModels");
}
};
mData.observe(this, mObserver);
}
Removing the observer onDestroy:
@Override
public void onDestroy() {
mData.removeObserver(mObserver);
super.onDestroy();
}
Following is my method in ViewModel:
public LiveData<List<TablesModel>> getTablesData(int mLocationID){
return mRepository.getTablesData(mLocationID);
}
Repository:
public LiveData<List<TablesModel>> getTablesData(int mLocationID){
LiveData<TablesModel[]> mTablesData = mDataSource.getTablesData();
mTablesData.observeForever(tablesModels -> {
mExecutors.diskIO().execute(() -> {
//Completed: delete old table data if there are conflicts.
if (tablesModels != null) {
mDatabaseDao.deleteTables();
mDatabaseDao.insertTablesData(tablesModels);
}else {
Log.e(LOG_TAG, "Nothing: ");
}
});
Log.e("Handlers", "repository getTablesData");
});
return mDatabaseDao.getTablesData(mLocationID);
}
DataSource:
private MutableLiveData<RestaurantTablesModel[]> mDownloadedTablesModel;
public LiveData<RestaurantTablesModel[]> getTablesData() {
Log.e("Handlers", "getTablesData");
fetchTablesData();
return mDownloadedTablesModel;
}
public void fetchTablesData() {
if (Utils.isNetworkAvailable(mContext)) {
NetworkUtils.NetworkInterface mInterface = this;
handler = new Handler();
runnableCode = new Runnable() {
@Override
public void run() {
// Do something here on the main thread
Log.e("Handlers", "Called on network thread");
URL getTablesURL = NetworkUtils.getAllTableUrl(mContext);
NetworkUtils.getResponseFromAPI(mContext, getTablesURL, mInterface);
// Repeat this the same runnable code block again another 30 seconds
// 'this' is referencing the Runnable object
handler.postDelayed(this, 30000);
}
};
handler.post(runnableCode);
} else {
Log.d(LOG_TAG, "fetchTablesData: No network!");
}
}
Now the problem is when my fragment is destroyed and recreated the Observer gets triggered multiple times, here are the logs:
09-05 10:28:29.853 3666-3666/? E/TablesFragment: setUpRecyclerView: tablesModels
09-05 10:28:30.039 3666-3666/? E/TablesFragment: setUpRecyclerView: tablesModels
09-05 10:28:30.607 3666-3666/? E/TablesFragment: setUpRecyclerView: tablesModels
09-05 10:28:30.657 3666-3666/? E/TablesFragment: setUpRecyclerView: tablesModels
09-05 10:28:30.669 3666-3666/? E/TablesFragment: setUpRecyclerView: tablesModels
09-05 10:28:30.704 3666-3666/? E/TablesFragment: setUpRecyclerView: tablesModels
And it triggers more times than before, every time fragment is recreated, I think it the observer is getting called on a recreation of the fragment and the previous instance of the observer is still in a play.
But if I'm removing the observer in OnDestroy, why should it be happening? Any help would be highly appreciated.
EDIT:
I changed my code to check if the LiveData and Observer are null, and then only initialize it. But it doesn't help, it is still getting called multiple times.
if (mTablesData == null){
mData = mViewModel.getTablesData(mLocationID);
if (mObserver == null){
mObserver = tablesModels -> {
if (tablesModels != null) {
mTablesRecyclerAdapter.addTables(tablesModels);
Log.e(LOG_TAG, "setUpUserRecyclerView: tablesModels");
}
};
mData.observe(this, mObserver);
}
}
EDIT 2:
Tried this also, but didn't work as well:
mTablesData = mViewModel.getTablesData(mLocationID);
mObserver = tablesModels -> {
if (tablesModels != null) {
mTablesRecyclerAdapter.addTables(tablesModels);
Log.e(LOG_TAG, "setUpRecyclerView: tablesModels");
}
};
if (!mTablesData.hasObservers()) {
mTablesData.observe(this, mObserver);
}
Upvotes: 2
Views: 1994
Reputation: 1563
First, If I understand correct you use RecyclerView
and every fragment in that RecyclerView
called setUpUserRecyclerView();
in its onCreate()
method. So, if you have 3 Fragments, you will have 3 Observers. If you want all of them to use the ViewModel
of the Activity
you have to point the parent Activity here -> ViewModelProviders.of(getActivity(), factory)
Second, Why do you use observeForever
in your Repository? Can you use just observe
?
And last if you want to run this request in every 30 seconds, why don't you use PeriodicWorkRequest
of WorkManager
-> https://developer.android.com/topic/libraries/architecture/workmanager/basics#java
Hope I help somehow :)
Upvotes: 1
Reputation: 1467
So what we learned from experiments in the comments, you needed to check if mTablesData
is already being observed before observing it, and observe only if it is not being observed, like
if (!mTablesData.hasObservers()) {
mTablesData.observeForever(tablesModels -> {
...
Upvotes: 2
Reputation: 789
I think you need wrap the mObserver in a CompositeDisposable.
CompositeDisposable disposable = new CompositeDisposable();
disposable.add(mObserver);
@Override
public void onDestroy() {
mData.removeObserver(mObserver);
disposable.clear();
super.onDestroy();
}
I hope it helps you.
Upvotes: 0