firebase / FirebaseUI-Android

Optimized UI components for Firebase
https://firebaseopensource.com/projects/firebase/firebaseui-android/
Apache License 2.0
4.63k stars 1.84k forks source link

App crashes when Minimise and Resumed #2045

Closed John-keno closed 7 months ago

John-keno commented 2 years ago

Welcome to FirebaseUI and thanks for submitting an issue!

Please take a look at open issues, as well as resolved issues, to see if your issue is either already being addressed, or has been solved by someone else.

If not, please feel free to fill in the following info so we can help faster!

Step 1: Are you in the right place?

Step 2: Describe your environment

Step 3: Describe the problem:

Whenever the fragment is populated with data in the recyclerView adapter and it is minimised, upon the restoration of the view app crashes.

Steps to reproduce:

  1. launch the app and wait for data to be populated to the recyclerView
  2. Minimise the app
  3. resume app

Observed Results:

Relevant Code:


import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.paging.PagingConfig;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;

import com.KejoTech.SBSureOdd.DAOPrediction;
import com.KejoTech.SBSureOdd.DataPrediction;
import com.KejoTech.SBSureOdd.R;
import com.KejoTech.SBSureOdd.databinding.FragmentPredictionBinding;
import com.firebase.ui.database.FirebaseRecyclerOptions;
import com.google.android.material.snackbar.Snackbar;
import com.google.firebase.database.DatabaseError;
import com.google.firebase.database.DatabaseReference;
import com.google.firebase.database.FirebaseDatabase;
import com.google.firebase.database.Query;

import java.util.Objects;

public class PredictionFragment extends Fragment {

    private static final String TAG = "prediction frag: ";
    private FragmentPredictionBinding binding;
    private RecyclerView recyclerView;
    private PredictionViewModel predictionViewModel;
    private ListAdapter listAdapter;
    private SwipeRefreshLayout swipe;

    public View onCreateView(@NonNull LayoutInflater inflater,
                             ViewGroup container, Bundle savedInstanceState) {

        binding = FragmentPredictionBinding.inflate(inflater, container, false);
        View root = binding.getRoot();

        recyclerView = binding.recyclerPredict;
        int c1 = getResources().getColor(R.color.accent);
        int c2 = getResources().getColor(R.color.primary_dark);
        swipe = binding.swipe;
        swipe.setColorSchemeColors(c1,c2);
        swipe.setRefreshing(true);
        setAdapter();

        recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
        recyclerView.setAdapter(listAdapter);

        swipe.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
            @Override
            public void onRefresh() {
                listAdapter.onDataChanged();
            }
        });

        return root;
    }

    private void setAdapter() {
        DatabaseReference databaseReference = FirebaseDatabase.getInstance().getReference();
        Query query = databaseReference.child("Data");

        FirebaseRecyclerOptions<DataPrediction> options =
                new FirebaseRecyclerOptions.Builder<DataPrediction>()
                        .setLifecycleOwner(getViewLifecycleOwner())
                        .setQuery(query, DataPrediction.class)
                        .build();
        listAdapter = new ListAdapter(options){
            @Override
            public void onDataChanged() {
                swipe.setRefreshing(false);
            }

            @Override
            public void onError(@NonNull DatabaseError error) {

            }
        };

    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        binding = null;
    }
}
thatfiredev commented 2 years ago

@John-keno thanks for opening this, I was able to reproduce the issue. A workaround that worked for me was to disable the item animator as suggested here:

recyclerView.setItemAnimator(null);

I think the fix should be implemented on the library itself, so that works as a temporary workaround for now. I'll continue to investigate how to fix this on the library and will post an update here once it's done.

John-keno commented 2 years ago

@thatfiredev I have a fix to it. Since it was a fragment I am using, I just placed the adapter listeners to start at onCreateView() and stop at onDestroyView(). I suggest that it should be implemented in the library also for the auto listeners so that they can be placed as mentioned above for fragments. The example code snippet below:

     public View onCreateView(@NonNull LayoutInflater inflater,
                             ViewGroup container, Bundle savedInstanceState) {
     // -------------After inflating view----------------------
        setAdapter();
        ListAdapter.startListening();
      return view;
    }

    public void onDestroyView() {
        super.onDestroyView();
        binding = null;
        ListAdapter.stopListening();
    } 
thatfiredev commented 2 years ago

@John-keno I'm glad you figured it out and thanks for letting me know what the problem was.

I'll be sure to update the library to address that :)

John-keno commented 2 years ago

@thatfiredev Thanks I'm anticipating that too 😊

Prateek-kannojia commented 1 year ago

@John-keno Thanks your code works like charm. I was stuck on it for a long time.