Parag Pawar
Parag Pawar

Reputation: 867

Architecture components: Observer keep observing even after removing it on onDestroy

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

Answers (3)

MrVasilev
MrVasilev

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

Deividas Strioga
Deividas Strioga

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

Juanjo Berenguer
Juanjo Berenguer

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

Related Questions