I am trying to develop a small Android application. The application is simple, after a login view I display an Activity that hosts a ListFragment. When the user clicks on an items of the list, I want to spawn a new Fragment, that contains the details of the clicked item. So, to achieve this, my code looks like this:

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        final FragmentManager fragmentManager = getFragmentManager();
        final Fragment listFragment = MyListFragment.newInstance(new MyItemClickListener() {
            @Override
            public void displayDetailsFor(Item item) {
                final FragmentTransaction transaction = fragmentManager.beginTransaction();
                transaction.replace(R.id.fragment_container, DetailsFragment.newInstance(status));
                transaction.addToBackStack(null);
                transaction.commit();
            }
        });

        final Fragment container = fragmentManager.findFragmentById(R.id.fragment_container);
        if (container == null) {
            final FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
            fragmentTransaction.add(R.id.fragment_container, listFragment);
            fragmentTransaction.commit();
        }
    }
}

The MyItemClickListener interface is just this:

public interface MyItemClickListener extends Serializable {
    void displayDetailsFor(Status status);
}

To complete the thing, in my list fragment, when the user selects an item, I just call the listener's method like this:

@Override
public void onListItemClick(ListView l, View v, int position, long id) {
    final Item item = (Item) getListAdapter().getItem(position);
    myItemClickListener.displayDetailsFor(item);
}

Everythings seems to work perfectly, the fragments with the details gets populated correctly and it takes the place of the list fragment. However, when I press the back button to pop the list fragment out of the back stack, an exception gets thrown. Here are the details:

java.lang.RuntimeException: Parcelable encountered IOException writing serializable object (name = package.MainActivity$1)
        at android.os.Parcel.writeSerializable(Parcel.java:1316)
        at android.os.Parcel.writeValue(Parcel.java:1264)
        at android.os.Parcel.writeArrayMapInternal(Parcel.java:618)
        at android.os.Bundle.writeToParcel(Bundle.java:1692)
        at android.os.Parcel.writeBundle(Parcel.java:636)
        at android.app.FragmentState.writeToParcel(Fragment.java:132)
        at android.os.Parcel.writeTypedArray(Parcel.java:1133)
        at android.app.FragmentManagerState.writeToParcel(FragmentManager.java:373)
        at android.os.Parcel.writeParcelable(Parcel.java:1285)
        at android.os.Parcel.writeValue(Parcel.java:1204)
        at android.os.Parcel.writeArrayMapInternal(Parcel.java:618)
        at android.os.Bundle.writeToParcel(Bundle.java:1692)
        at android.os.Parcel.writeBundle(Parcel.java:636)
        at android.app.ActivityManagerProxy.activityStopped(ActivityManagerNative.java:2467)
        at android.app.ActivityThread$StopInfo.run(ActivityThread.java:3098)
        at android.os.Handler.handleCallback(Handler.java:733)
        at android.os.Handler.dispatchMessage(Handler.java:95)
        at android.os.Looper.loop(Looper.java:136)
        at android.app.ActivityThread.main(ActivityThread.java:5017)
        at java.lang.reflect.Method.invokeNative(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:515)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:779)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:595)
        at dalvik.system.NativeStart.main(Native Method)
 Caused by: java.io.NotSerializableException: package.MainActivity
        at java.io.ObjectOutputStream.writeNewObject(ObjectOutputStream.java:1364)
        at java.io.ObjectOutputStream.writeObjectInternal(ObjectOutputStream.java:1671)
        at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:1517)
        at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:1481)
        at java.io.ObjectOutputStream.writeFieldValues(ObjectOutputStream.java:979)
        at java.io.ObjectOutputStream.defaultWriteObject(ObjectOutputStream.java:368)
        ...

I tried to solve this problem by adding a back button handler:

@Override
public void onBackPressed() {
    final FragmentManager fragmentManager = getFragmentManager();
    if (fragmentManager.getBackStackEntryCount() > 0) {{
        fragmentManager.popBackStack();
    }
}

and seems to be working fine, but when I'm in the list view again and I press the back button there, the same exception gets thrown. Am I missing something?

Thanks!

有帮助吗?

解决方案

Although the call stack is incomplete, the problem is likely to be related to the usage of the MyListFragment.newInstance() Fragment instantiation pattern. If you are using setArguments() and passing the OnClickListener you just created, it has a reference to the Activity class (since it's a non-static inner class of the Activity).

Activity classes are not serializable, hence the exception when the Bundle is built.

I would suggest having the Activity implement the MyItemClickListener interface (no longer Serializable) and invoking that from the fragment. For example, in the click listener, do:

((MyItemClickListener)getActivity()).displayDetailsFor(status).

For an example, you can see http://www.vogella.com/tutorials/AndroidFragments/article.html#fragments_activitycommunication

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top