NotSerializableException when FragmentActivity goes to background in Android
Asked Answered
R

1

8

I have 5 fragments inside my activity where one fragment stays active at one time. Clicking on a recyclerview item opens another fragment and puts current fragment in the backstack.
The same code was working fine some days ago, but now the app is throwing NotSerializableException whenever I click the home button to put the app in background. I have tried putting the initializing the variables inside onStart and then giving the null value in onStop but that didn't work.
Fragment Code:

public class PaperListFragment extends Fragment implements Serializable {

private static final String TAG = "PaperListFragment";
private static final String QUESTIONS_FRAGMENT_TAG = "questions_fragment";
private static final String ADD_PAPER_FRAGMENT_TAG = "add_paper_fragment";

private OnFragmentActiveListener mOnFragmentActiveListener;
private TextView mHeadingText;
private Bundle mOutState;
private FirebaseAuth mAuth;
private DatabaseReference mDatabaseReference;
private ProgressBar mProgressBar;
private OnItemClickListener mOnItemClickListener;
private FloatingActionButton mFab;
private RecyclerView mRecyclerViewPaper;
private ArrayList<Paper> mPaperList = new ArrayList<>();
private Subject mSubject = new Subject();
private Exam mExam = new Exam();

@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    View rootView = inflater.inflate(R.layout.fragment_recycler_list, container, false);


    mProgressBar = (ProgressBar) rootView.findViewById(R.id.progressbar_news);
    mFab = (FloatingActionButton) rootView.findViewById(R.id.floatingActionButton);
    mProgressBar.setVisibility(View.VISIBLE);
    Log.d(TAG, "onCreateView: Fragment created");
    mAuth = FirebaseAuth.getInstance();
    mDatabaseReference = FirebaseDatabase.getInstance().getReference();
    if (mAuth.getCurrentUser() == null) {
        startActivity(new Intent(getActivity(), LoginActivity.class));
        getActivity().finish();
        return null;
    }

    if (getArguments() != null) {
        mOnFragmentActiveListener = (OnFragmentActiveListener) getArguments().getSerializable(Keys.FRAGMENT_ACTIVE_LISTENER);
        mSubject = (Subject) getArguments().getSerializable(Keys.SUBJECT_KEY);
        mExam = (Exam) getArguments().getSerializable(Keys.EXAMS_KEY);
    }

    mRecyclerViewPaper = (RecyclerView) rootView.findViewById(R.id.recycler_list);
    LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity()) {
        @Override
        public boolean canScrollVertically() {
            return false;
        }
    };
    mRecyclerViewPaper.setLayoutManager(layoutManager);
    Log.d(TAG, "onCreateView: Layout Manager Set.");

    mFab.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            startAddPaperFragment();
        }
    });

    mOnItemClickListener = new OnItemClickListener() {
        @Override
        public void onItemClicked(RecyclerView.ViewHolder holder, int position) {
            Log.d(TAG, "onItemClicked: Clicked item position is: "+ position);
            QuestionListFragment questionFragment = new QuestionListFragment();
            questionFragment.setRetainInstance(true);

            startFragment(position, questionFragment, QUESTIONS_FRAGMENT_TAG);

        }

        @Override
        public void OnItemLongClicked(RecyclerView.ViewHolder holder, int position) {

        }
    };

    mHeadingText = (TextView) rootView.findViewById(R.id.heading_textview);
    mHeadingText.setText(mExam.getExam_name()+" > "+ mSubject.getSubject_name());

    if (mOutState != null) {
        mPaperList = (ArrayList<Paper>) mOutState.getSerializable(Keys.PAPER_LIST_KEY);
        updateUI();
    } else {
        updateUIFromDatabase();
    }


    return rootView;
}

private void startFragment(int position, Fragment fragment, String fragmentTag) {
    Paper paper = new Paper();
    if (mPaperList.size() > 0) {
        paper = mPaperList.get(position);
    }
    Bundle args = new Bundle();
    args.putSerializable(Keys.EXAMS_KEY, mExam);
    args.putSerializable(Keys.SUBJECT_KEY, mSubject);
    args.putSerializable(Keys.PAPER, paper);
    args.putSerializable(Keys.FRAGMENT_ACTIVE_LISTENER, mOnFragmentActiveListener);
    fragment.setArguments(args);
    FragmentTransaction fragmentTransaction = getActivity().getSupportFragmentManager().beginTransaction();
    fragmentTransaction.setCustomAnimations(R.anim.slide_in_right, R.anim.slide_out_left, R.anim.slide_in_left, R.anim.slide_out_right);

    fragmentTransaction.replace(R.id.questions_fragment_container, fragment, fragmentTag);
    fragmentTransaction.addToBackStack(fragmentTag);
    fragmentTransaction.commit();
}

private void startAddPaperFragment() {
    AddPaperFragment addPaperFragment = new AddPaperFragment();
    addPaperFragment.setRetainInstance(true);
    startFragment(0, addPaperFragment, ADD_PAPER_FRAGMENT_TAG);
}

private void updateUIFromDatabase() {
    if (getArguments() != null){
        Exam exam = (Exam) getArguments().getSerializable(Keys.EXAMS_KEY);
        Subject subject = (Subject) getArguments().getSerializable(Keys.SUBJECT_KEY);
        DatabaseReference paperReference =
                mDatabaseReference
                .child(Keys.APP_DATA_KEY)
                .child(Keys.EXAM_PAPERS)
                .child(exam.getExam_name())
                .child(subject.getSubject_name());
        Query query = paperReference.orderByChild(Keys.TIME_ADDED);
        query.addValueEventListener(new ValueEventListener() {
            @Override
            public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
                mPaperList.clear();
                for (DataSnapshot paperChild : dataSnapshot.getChildren()) {
                    mPaperList.add(paperChild.getValue(Paper.class));
                }

                updateUI();
            }

            @Override
            public void onCancelled(@NonNull DatabaseError databaseError) {

            }
        });
    }
}

private void updateUI() {
    PaperRecyclerAdapter adapter = new PaperRecyclerAdapter(
            getActivity(),
            mRecyclerViewPaper,
            mPaperList,
            mOnItemClickListener
    );
    mRecyclerViewPaper.setAdapter(adapter);
    mProgressBar.setVisibility(View.GONE);
}

@Override
public void onResume() {
    super.onResume();
    if (getArguments()!=null){
        mOnFragmentActiveListener.onFragmentActive(
                this,
                "Topics"
        );
    }
}

@Override
public void onPause() {
    super.onPause();
    mOutState = new Bundle();
    mOutState.putSerializable(Keys.PAPER_LIST_KEY, mPaperList);
}

}

Exception:

2018-12-26 17:49:38.344 14834-14834/in.crazybytes.bankmaniaadmin E/AndroidRuntime: FATAL EXCEPTION: main
Process: in.crazybytes.bankmaniaadmin, PID: 14834
java.lang.RuntimeException: Parcelable encountered IOException writing serializable object (name = in.crazybytes.bankmaniaadmin.activities.QuestionsActivity)
    at android.os.Parcel.writeSerializable(Parcel.java:1526)
    at android.os.Parcel.writeValue(Parcel.java:1474)
    at android.os.Parcel.writeArrayMapInternal(Parcel.java:723)
    at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1408)
    at android.os.Bundle.writeToParcel(Bundle.java:1133)
    at android.os.Parcel.writeBundle(Parcel.java:763)
    at android.support.v4.app.FragmentState.writeToParcel(FragmentState.java:124)
    at android.os.Parcel.writeTypedArray(Parcel.java:1306)
    at android.support.v4.app.FragmentManagerState.writeToParcel(FragmentManager.java:639)
    at android.os.Parcel.writeParcelable(Parcel.java:1495)
    at android.os.Parcel.writeValue(Parcel.java:1401)
    at android.os.Parcel.writeArrayMapInternal(Parcel.java:723)
    at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1408)
    at android.os.Bundle.writeToParcel(Bundle.java:1133)
    at android.os.Parcel.writeBundle(Parcel.java:763)
    at android.app.ActivityManagerProxy.activityStopped(ActivityManagerNative.java:3697)
    at android.app.ActivityThread$StopInfo.run(ActivityThread.java:3768)
    at android.os.Handler.handleCallback(Handler.java:751)
    at android.os.Handler.dispatchMessage(Handler.java:95)
    at android.os.Looper.loop(Looper.java:154)
    at android.app.ActivityThread.main(ActivityThread.java:6123)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:867)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:757)
 Caused by: java.io.NotSerializableException: com.google.firebase.auth.internal.zzj
    at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1224)
    at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1584)
    at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1549)
    at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1472)
    at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1218)
    at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1584)
    at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1549)
    at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1472)
    at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1218)
    at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:346)
    at android.os.Parcel.writeSerializable(Parcel.java:1521)
    at android.os.Parcel.writeValue(Parcel.java:1474) 
    at android.os.Parcel.writeArrayMapInternal(Parcel.java:723) 
    at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1408) 
    at android.os.Bundle.writeToParcel(Bundle.java:1133) 
    at android.os.Parcel.writeBundle(Parcel.java:763) 
    at android.support.v4.app.FragmentState.writeToParcel(FragmentState.java:124) 
    at android.os.Parcel.writeTypedArray(Parcel.java:1306) 
    at android.support.v4.app.FragmentManagerState.writeToParcel(FragmentManager.java:639) 
    at android.os.Parcel.writeParcelable(Parcel.java:1495) 
    at android.os.Parcel.writeValue(Parcel.java:1401) 
    at android.os.Parcel.writeArrayMapInternal(Parcel.java:723) 
    at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1408) 
    at android.os.Bundle.writeToParcel(Bundle.java:1133) 
    at android.os.Parcel.writeBundle(Parcel.java:763) 
    at android.app.ActivityManagerProxy.activityStopped(ActivityManagerNative.java:3697) 
    at android.app.ActivityThread$StopInfo.run(ActivityThread.java:3768) 
    at android.os.Handler.handleCallback(Handler.java:751) 
    at android.os.Handler.dispatchMessage(Handler.java:95) 
    at android.os.Looper.loop(Looper.java:154) 
    at android.app.ActivityThread.main(ActivityThread.java:6123) 
    at java.lang.reflect.Method.invoke(Native Method) 
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:867) 
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:757)

Note: The weird thing is that one of fragment has the exact same code and is hosted inside the same activity, but when that fragment is active and app goes to background, interestingly the app is not crashing. 

**Exam Model Class: package in.crazybytes.bankmaniaadmin.models;

import java.io.Serializable;

public class Exam implements Serializable {
private String mExam_name;
private String mExam_key;
private Long mTime_added;
private int mNum_subjects;
private int mNum_questions;

public Exam(String exam_name, String exam_key, Long time_added, int num_subjects, int num_questions) {
    mExam_name = exam_name;
    mExam_key = exam_key;
    mTime_added = time_added;
    mNum_subjects = num_subjects;
    mNum_questions = num_questions;
}

public Exam() {
}

public String getExam_name() {
    return mExam_name;
}

public void setExam_name(String exam_name) {
    mExam_name = exam_name;
}

public String getExam_key() {
    return mExam_key;
}

public void setExam_key(String exam_key) {
    mExam_key = exam_key;
}

public Long getTime_added() {
    return mTime_added;
}

public void setTime_added(Long time_added) {
    mTime_added = time_added;
}

public int getNum_subjects() {
    return mNum_subjects;
}

public void setNum_subjects(int num_subjects) {
    mNum_subjects = num_subjects;
}

public int getNum_questions() {
    return mNum_questions;
}

public void setNum_questions(int num_questions) {
    mNum_questions = num_questions;
}

}

Paper Model Class

package in.crazybytes.bankmaniaadmin.models;

import java.io.Serializable;

public class Paper implements Serializable {
private String mPaper_name;
private String mPaper_key;
private Long mTime_added;
private int mNum_questions;

public Paper(String paper_name, String paper_key, Long time_added, int num_questions) {
    mPaper_name = paper_name;
    mPaper_key = paper_key;
    mTime_added = time_added;
    mNum_questions = num_questions;
}

public Paper() {
}

public String getPaper_key() {
    return mPaper_key;
}

public void setPaper_key(String paper_key) {
    mPaper_key = paper_key;
}

public Long getTime_added() {
    return mTime_added;
}

public void setTime_added(Long time_added) {
    mTime_added = time_added;
}

public int getNum_questions() {
    return mNum_questions;
}

public void setNum_questions(int num_questions) {
    mNum_questions = num_questions;
}

public String getPaper_name() {
    return mPaper_name;
}

public void setPaper_name(String paper_name) {
    mPaper_name = paper_name;
}

}

Subject Model Class:

package in.crazybytes.bankmaniaadmin.models;

import java.io.Serializable;

public class Subject implements Serializable {
private String mSubject_name;
private String mSubject_key;
private Long mTime_added;
private int mNum_papers;
private int mNum_questions;

public Subject(String subject_name, String subject_key, Long time_added, int num_papers, int num_questions) {
    mSubject_name = subject_name;
    mSubject_key = subject_key;
    mTime_added = time_added;
    mNum_papers = num_papers;
    mNum_questions = num_questions;
}

public Subject() {
}

public String getSubject_name() {
    return mSubject_name;
}

public void setSubject_name(String subject_name) {
    mSubject_name = subject_name;
}

public String getSubject_key() {
    return mSubject_key;
}

public void setSubject_key(String subject_key) {
    mSubject_key = subject_key;
}

public Long getTime_added() {
    return mTime_added;
}

public void setTime_added(Long time_added) {
    mTime_added = time_added;
}

public int getNum_papers() {
    return mNum_papers;
}

public void setNum_papers(int num_papers) {
    mNum_papers = num_papers;
}

public int getNum_questions() {
    return mNum_questions;
}

public void setNum_questions(int num_questions) {
    mNum_questions = num_questions;
}

}

Reformism answered 26/12, 2018 at 12:33 Comment(6)
can you add your Exam & Subject model classCongeneric
I have implemented Serializable in those model classes and also all other fragments, models, interfaces, activities and classes are also implementing SerializableReformism
Added The Exam, Subject and Paper model classesReformism
It gives NotSerializableException even on TextViews and FloatingActionButtons.Reformism
Where is OnFragmentActiveListener defined? Is it an inner class of QuestionsActivity?Tevet
It was defined outside, but my QuestionActivity was implementing it. That was the issue. Thanks for helping me.Reformism
T
8

Somehow QuestionsActivity is getting into the fragment save state, even if you don't intend for that to happen. While QuestionsActivity is being serialized, another object that is not serializable is being encountered. That's why you see TextViews and other things attempting to get serialized because all the instance variables of QuestionsActivity get serialized by default.

My best guess for why this is happening is due to this line:

args.putSerializable(Keys.FRAGMENT_ACTIVE_LISTENER, mOnFragmentActiveListener);

But it's difficult to know for sure without seeing where OnFragmentActiveListener is defined. I'm assuming either QuestionsActivity implements OnFragmentActiveListener, or QuestionsActivity defines OnFragmentActiveListener as an inner class. Either way, if you put an OnFragmentActiveListener into fragment arguments, you will encounter an exception because you indirectly are storing the entire QuestionsActivity as a fragment arg too. When a fragment stops, all fragment args become part of the fragment save state. And that's the cause of the error.

I would suggest not passing the OnFragmentActiveListener around as a fragment arg. If the OnFragmentActiveListener comes from the activity, just use getActivity() to get a reference to the activity and then get a reference to the listener.

I also noticed PaperListFragment implements Serializable and I'm assuming you did the same thing for QuestionsActivity. You probably did this to get around compile errors. But this has led to runtime errors because the instance variables on both of these classes are not all serializable. So to avoid more runtime issues, I would suggest never having activities or fragments implement serializable because these classes are inherently not serializable due to their members.

Tevet answered 26/12, 2018 at 17:58 Comment(1)
Thanks a lot @Greg Moens. Yes, my QuestionsActivity was implementing OnFragmentActiveListener and then I was putting 'this' as Serializable and getting it in the fragments. Solved the issue, by declaring the OnFragmentListener as variable and then setting it using a public setter in each fragment.Reformism

© 2022 - 2024 — McMap. All rights reserved.