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

android livedata remove observer
android-livedata observer called multiple times
livedata observer not called
android-livedata multiple observers
livedata observe once
clear viewmodel android
observeforever
android-livedata clear value

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);
    }

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 -> { ...

LiveData Overview, object so that it is notified of changes. You usually attach the Observer object in a UI controller, such as an activity or fragment. 1 Architecture components: Observer keep observing even after removing it on onDestroy Sep 5 '18 1 How does fragment's lifecycle works inside viewpager? Why onStop is not called on navigation change?

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 :)

5 common mistakes when using Architecture Components, Subtle oversights with more or less serious consequences - even if you're not making these mistakes it should be worth keeping them in mind to This means that if we start observing LiveData in onCreateView() or later but LiveData won'​t remove previous observers, because LifecycleOwner (Fragment)  There are 4 Android Architecture Components : Lifecycle LiveData ViewModel Room Lifecycle Lifecycle class helps us in building lifecycle aware components. It is a class which holds all the information about the states of an activity or a fragment. It allows other objects to observe lifecycle states like a resume, pause etc.

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.

Fragments create new instances of Observers after , After trying to create a project, making use of the new arch components. Of course, another solution is to keep the views around when onDestroyView is called but https://github.com/googlesamples/android-architecture-components/​issues/47 I tried removing the attached Observer of the LiveData after  The Architecture Components provide default ViewModelProvider implementations for activities and fragments. They allow you to store LiveData instances inside a ViewModel to be reused across configuration changes.

Architecture Components pitfalls, The Architecture Components provide default ViewModelProvider instance will be added in onViewCreated() , while the previous one has not been removed! code: LiveData keeps track of which result has been delivered to which observer, state of the fragment, even when no view hierarchy is currently attached to it. After all, when the ViewModel's user field is set, we need a way to inform the UI. This is where the LiveData class comes in. LiveData is an observable data holder. It lets the components in your app observe LiveData objects for changes without creating explicit and rigid dependency paths between them.

Internals of Android Architecture Components Part II— LiveData, This post series explores the implementation of Android Architecture This awareness ensures LiveData only updates app component observers that are in an active When the lifecycle is not in the DESTROYED state, observe creates a update mData to hold the latest data, and notify the observers. android/architecture-components-samples#47 As pointed out in that blogpost, everything's fine if a fragment is destroyed (after a rotation for example), however if it's detached/attached then the observer isn't removed and a new one is created every time - unless we use `viewLifecycleOwner` instead of `this` as the observation's owner.

Observe LiveData from ViewModel in Fragment - Noteworthy, Google introduced Android architecture components which are basically a This is because the observers were not getting removed when Since its more of a workaround and would be difficult to maintain(each Observe requires is added to backstack, the state of FragmentA lifecycle is onDestroyView . Architecture Components is a new library by Google that has the aim to help us design application that are “robust, testable, and maintainable”. In a nutshell, this library helps us to better handle the persisting of data across lifecycle events and configuration changes, whilst also helping us to create

Comments
  • Is the onDestroy method even called? Did you tried to set a breakpoint to see if the observer is really removed?
  • I just put LOG in onDestroy, and yes it is getting called
  • As @pskink mentionend, maybe your method is called multiple times. Check if mData & mObserver is null before you are creating these variabels, to prevent recreation. But this method should be called only once.
  • Also try doing the same in your repo: mTablesData.observeForever(tablesModels -> {.... It looks like a root cause, the use of mTablesData.observeForever is unadvisable.
  • You don't need mData.removeObserver(mObserver); on destroy as that is the function of live data, they unsubscribe automatically. BTW, how do you set the downloaded data to mDownloadedTablesModel?
  • No, I have only one fragment that does have a RecyclerView. Anyway, I solved it, using Deividas Strioga's help. But thanks for telling me about PeriodicWorkRequest I'd definitely check that out
  • Cool, happy to help at least with one thing
  • Is it related to RxJAVA? Cause I'm not using it
  • Sorry I thought the observer was a rxjava observer