简体   繁体   English

添加自己的接口后,Java会抛出NotSerializableException

[英]Java throws NotSerializableException after adding own interface

People, I'm getting quite stuck on a new Android application I'm developing. 人们,我正在坚持我正在开发的新Android应用程序。 For the application (card game) I need to save some data. 对于应用程序(纸牌游戏),我需要保存一些数据。 I use Serialization to get that done. 我使用序列化来完成这项工作。

Now the problem: When I try to implement a interface I made to keep track of the payers turn, the application returns a NoSerializableException from the class Game (main activity). 现在的问题是:当我尝试实现一个跟踪付款人转向的界面时,应用程序从类Game(主要活动)返回NoSerializableException。 Every thing works when I remove the interface. 当我删除界面时,每件事都有效。

The turn class contains the following code: 转弯类包含以下代码:

public class Turn<T> implements Serializable{

/**
 * 
 */
private static final long serialVersionUID = 1L;

public interface OnTurnEndedListener<T>{
    void onTurnEnded(T currentPlayer);
}

private ArrayList<T> players;
private int turnIndex;
private int rounds;
private ArrayList<OnTurnEndedListener<T>> turnEndListenerList;

public Turn() {
    throw new UnsupportedOperationException("cannot init without players");
}

public Turn(ArrayList<T> players, int startingPlayerIndex) {
    this.players = players;
    this.turnIndex = startingPlayerIndex;
    this.rounds = 0;
    turnEndListenerList = new ArrayList<OnTurnEndedListener<T>>();
}

public int getRounds() {
    return rounds;
}

public T next() {
    turnIndex = (turnIndex + 1) % players.size();
    if (turnIndex == 0) {
        rounds++;
    }
    T retVal = players.get(turnIndex);
    for (OnTurnEndedListener<T> l : turnEndListenerList) {
        l.onTurnEnded(retVal);
    }
    return retVal;
}

public T peek() {
    return players.get(turnIndex);
}

public void addOnTurnEndedListener(OnTurnEndedListener<T> l) {
    this.turnEndListenerList.add(l);

}
}

When I add the following code in the main activity (Game) I get an exception every time I close the activity. 当我在主要活动(游戏)中添加以下代码时,每次关闭活动时都会收到异常。

gameData.getTurn().addOnTurnEndedListener(new Turn.OnTurnEndedListener<Hand>() {
            @Override
            public void onTurnEnded(Hand hand) {
                turnEndedHandler(hand);
            }
        });

You can find the full code of the Game and GameData class, as well as the error log below. 您可以找到Game和GameData类的完整代码,以及下面的错误日志。

import java.util.ArrayList;
import java.util.Collections;

import android.app.Activity;
import android.content.Intent;
import android.content.res.Configuration;
import android.os.Bundle;
import android.util.Log;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnTouchListener;
import android.view.ViewGroup.LayoutParams;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;

public class Game extends Activity implements OnTouchListener{

    private Deck deck;
    private GameData gameData;
    Hand playerHand, oppHand;
    private ImageView ivDeckClosed, ivDeckOpen, ivPlayerCard1, ivPlayerCard2,ivPlayerCard3, ivPlayerCard4, ivPlayerCard5, ivPlayerCard6,ivPlayerCard7, ivPlayerCard8, ivPlayerCard9, ivPlayerCard10,ivPlayerCard11, ivPlayerCard12, ivPlayerCard13, ivPlayerCard14;
    private ImageView[] playerCards;
    private TextView tvOpp1;
    private ArrayList<Hand> playersInOrder;
    private LinearLayout llPlayGround,llPlayGroundRow1,llPlayGroundRow2,llCardDeck;
    private ArrayList<PlayedSet> playedSets;
    public static final String SAVE_FILENAME = "jokerensave.ser";
    private SaveHandler savehandler;
    private Hand currentHand;
    private int defaultStartingPlayer = 0;

    public static enum STATES {
        start, resume, end
    };

    public static final int START_CODE = 0;
    public static final int RESUME_CODE = 1;
    public static final String GAME_STATE = "STATE";
    public static final String GAME_DATA = "GameData";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d("PUKI","onCreate");

        // Get save game
        savehandler = SaveHandler.getInstance(this);
        gameData = savehandler.readLastState();

        setContentView(R.layout.gamescreen);

        // Load which state was given by the mainscreen
        switch ((STATES) getIntent().getExtras().get(GAME_STATE)) {
        case start:
            gameData.setFirstRun(true);
            Log.i("ONCREATE", "Received state: start");
            break;
        case resume:
            gameData.setFirstRun(false);
            Log.i("ONCREATE", "Received state: resume");
            break;
        default:
            gameData.setFirstRun(true);
            Log.i("ONCREATE", "Received state: none");
            break;
        }

        // Transferring game data to MainScreen
        Bundle b = new Bundle();
        b.putInt("int", 5);
        b.putSerializable(GAME_DATA, gameData);
        Intent i = new Intent();
        i.putExtras(b);
        setResult(0, i);
    }

    @Override
    protected void onStart() {
        super.onStart();
        Log.d("PUKI","onStart");
        Log.i("FIRSTRUN", "Firstrun = "+gameData.getFirstRun());

        init(gameData.getFirstRun());


        gameData.getTurn().addOnTurnEndedListener(new Turn.OnTurnEndedListener<Hand>() {
            @Override
            public void onTurnEnded(Hand hand) {
                turnEndedHandler(hand);
            }
        });
    }

    private void init(boolean first) {
        initGraphics(first);
        Log.i("INIT", "Game init graphics");
        if (first) {
            Log.i("INIT", "Game init core");
            initGameCore();
        }
    }

    private void initGameCore() {
        deck = new Deck();
        playedSets = new ArrayList<PlayedSet>();

        // Create array with players and their hand
        playersInOrder = new ArrayList<Hand>();
        playerHand = new PlayerHand(playerCards, "Player name");
        playersInOrder.add(playerHand);
        oppHand = new OppHand(new GameStrategy(), null, "Opponent");
        playersInOrder.add(oppHand);

        // Push all data to gamedata class
        gameData.init(playerHand, oppHand, playersInOrder, deck, playedSets, new Turn<Hand>(playersInOrder,defaultStartingPlayer));
        gameData.setGameInProgress(true);

        // Deal cards to players
        dealCards();
    }


    //TODO
    protected void turnEndedHandler(final Hand hand) {
        if(hand.isAwaitingInput()){
            // This means the turn is for a human player, so do nothing.
            Log.i("TURN", "The turn is for the human player: "+hand.getPlayerName());
        }
        else{
            // This means the turn is for a AI. Decide!
            Log.i("TURN", "The turn is for the AI player: "+hand.getPlayerName());
            gameData.getTurn().next();

            // Update players hand size for human player
            this.updateOppScore();
        }
    }

(I removed a lot of code from the above example, because that code isn't needed to solve this problem) (我从上面的例子中删除了很多代码,因为解决这个问题不需要代码)

import java.io.Serializable;
import java.util.ArrayList;

public class GameData implements Serializable {

    /**
     * 
     */
    private static final long serialVersionUID = -3796450525724090900L;

    private Hand playerHand, oppHand;
    private ArrayList<Hand> playersInOrder;
    private Deck deck;
    private boolean gameInProgress,grabbedCard,playerMustThrow,firstRun;
    private ArrayList<PlayedSet> playedSets; 
    private Turn<Hand> turn;

    private static GameData instance = new GameData();

    public GameData(){
        // Do nothing
    }

    public static GameData getInstance(){
        return instance;
    }

    public void init(Hand playerHand, Hand oppHand, ArrayList<Hand> playersInOrder, Deck deck, ArrayList<PlayedSet> playedSets, Turn<Hand> turn) {
        this.playerHand = playerHand;
        this.playersInOrder = playersInOrder;
        this.oppHand = oppHand;
        this.deck = deck;
        this.grabbedCard = false;
        this.playerMustThrow = false;
        this.playedSets = playedSets;
        this.firstRun = false;
        this.turn = turn;
    }

    public Hand getPlayerHand(){
        return playerHand;
    }
    public Hand getOppHand(){
        return oppHand;
    }
    public Deck getDeck(){
        return deck;
    }
    public ArrayList<Hand> getPlayersInOrder(){
        return playersInOrder;
    }
    public void setGrabbedCard(boolean set){
        this.grabbedCard = set;
    }
    public boolean getGrabbedCard(){
        return grabbedCard;
    }

    public void setGameInProgress(boolean progress) {
        this.gameInProgress = progress;
    }

    public boolean isGameInProgress(){
        return gameInProgress;
    }

    public void createNewPlaySet(PlayedSet newSet){
        playedSets.add(newSet);
    }

    public ArrayList<PlayedSet> getAllPlayedSets(){
        return playedSets;
    }

    public void setPlayerCanThrow(boolean set){
        this.playerMustThrow = set;
    }

    public boolean canPlayerThrow(){
        return playerMustThrow;
    }

    public boolean getFirstRun(){
        return firstRun;
    }

    public void setFirstRun(boolean set){
        this.firstRun = set;
    }

    public Turn<Hand> getTurn(){
        return turn;
    }

}

Log: 日志:

01-20 21:05:16.678: W/dalvikvm(27035): threadid=1: thread exiting with uncaught exception (group=0x40c5c1f8)
01-20 21:05:16.693: E/AndroidRuntime(27035): FATAL EXCEPTION: main
01-20 21:05:16.693: E/AndroidRuntime(27035): java.lang.RuntimeException: Parcelable encountered IOException writing serializable object (name = nl.dirkgroenen.jokeren.GameData)
01-20 21:05:16.693: E/AndroidRuntime(27035):    at android.os.Parcel.writeSerializable(Parcel.java:1181)
01-20 21:05:16.693: E/AndroidRuntime(27035):    at android.os.Parcel.writeValue(Parcel.java:1135)
01-20 21:05:16.693: E/AndroidRuntime(27035):    at android.os.Parcel.writeMapInternal(Parcel.java:493)
01-20 21:05:16.693: E/AndroidRuntime(27035):    at android.os.Bundle.writeToParcel(Bundle.java:1612)
01-20 21:05:16.693: E/AndroidRuntime(27035):    at android.os.Parcel.writeBundle(Parcel.java:507)
01-20 21:05:16.693: E/AndroidRuntime(27035):    at android.content.Intent.writeToParcel(Intent.java:6224)
01-20 21:05:16.693: E/AndroidRuntime(27035):    at android.app.ActivityManagerProxy.finishActivity(ActivityManagerNative.java:1831)
01-20 21:05:16.693: E/AndroidRuntime(27035):    at android.app.Activity.finish(Activity.java:3709)
01-20 21:05:16.693: E/AndroidRuntime(27035):    at android.app.Activity.onBackPressed(Activity.java:2124)
01-20 21:05:16.693: E/AndroidRuntime(27035):    at android.app.Activity.onKeyUp(Activity.java:2099)
01-20 21:05:16.693: E/AndroidRuntime(27035):    at android.view.KeyEvent.dispatch(KeyEvent.java:2633)
01-20 21:05:16.693: E/AndroidRuntime(27035):    at android.app.Activity.dispatchKeyEvent(Activity.java:2334)
01-20 21:05:16.693: E/AndroidRuntime(27035):    at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1958)
01-20 21:05:16.693: E/AndroidRuntime(27035):    at android.view.ViewRootImpl.deliverKeyEventPostIme(ViewRootImpl.java:3565)
01-20 21:05:16.693: E/AndroidRuntime(27035):    at android.view.ViewRootImpl.handleFinishedEvent(ViewRootImpl.java:3538)
01-20 21:05:16.693: E/AndroidRuntime(27035):    at android.view.ViewRootImpl.handleMessage(ViewRootImpl.java:2646)
01-20 21:05:16.693: E/AndroidRuntime(27035):    at android.os.Handler.dispatchMessage(Handler.java:99)
01-20 21:05:16.693: E/AndroidRuntime(27035):    at android.os.Looper.loop(Looper.java:137)
01-20 21:05:16.693: E/AndroidRuntime(27035):    at android.app.ActivityThread.main(ActivityThread.java:4511)
01-20 21:05:16.693: E/AndroidRuntime(27035):    at java.lang.reflect.Method.invokeNative(Native Method)
01-20 21:05:16.693: E/AndroidRuntime(27035):    at java.lang.reflect.Method.invoke(Method.java:511)
01-20 21:05:16.693: E/AndroidRuntime(27035):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:980)
01-20 21:05:16.693: E/AndroidRuntime(27035):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:747)
01-20 21:05:16.693: E/AndroidRuntime(27035):    at dalvik.system.NativeStart.main(Native Method)
01-20 21:05:16.693: E/AndroidRuntime(27035): Caused by: java.io.NotSerializableException: nl.dirkgroenen.jokeren.Game$6
01-20 21:05:16.693: E/AndroidRuntime(27035):    at java.io.ObjectOutputStream.writeNewObject(ObjectOutputStream.java:1364)
01-20 21:05:16.693: E/AndroidRuntime(27035):    at java.io.ObjectOutputStream.writeObjectInternal(ObjectOutputStream.java:1671)
01-20 21:05:16.693: E/AndroidRuntime(27035):    at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:1517)
01-20 21:05:16.693: E/AndroidRuntime(27035):    at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:1481)
01-20 21:05:16.693: E/AndroidRuntime(27035):    at java.util.ArrayList.writeObject(ArrayList.java:644)
01-20 21:05:16.693: E/AndroidRuntime(27035):    at java.lang.reflect.Method.invokeNative(Native Method)
01-20 21:05:16.693: E/AndroidRuntime(27035):    at java.lang.reflect.Method.invoke(Method.java:511)
01-20 21:05:16.693: E/AndroidRuntime(27035):    at java.io.ObjectOutputStream.writeHierarchy(ObjectOutputStream.java:1053)
01-20 21:05:16.693: E/AndroidRuntime(27035):    at java.io.ObjectOutputStream.writeNewObject(ObjectOutputStream.java:1404)
01-20 21:05:16.693: E/AndroidRuntime(27035):    at java.io.ObjectOutputStream.writeObjectInternal(ObjectOutputStream.java:1671)
01-20 21:05:16.693: E/AndroidRuntime(27035):    at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:1517)
01-20 21:05:16.693: E/AndroidRuntime(27035):    at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:1481)
01-20 21:05:16.693: E/AndroidRuntime(27035):    at java.io.ObjectOutputStream.writeFieldValues(ObjectOutputStream.java:979)
01-20 21:05:16.693: E/AndroidRuntime(27035):    at java.io.ObjectOutputStream.defaultWriteObject(ObjectOutputStream.java:368)
01-20 21:05:16.693: E/AndroidRuntime(27035):    at java.io.ObjectOutputStream.writeHierarchy(ObjectOutputStream.java:1074)
01-20 21:05:16.693: E/AndroidRuntime(27035):    at java.io.ObjectOutputStream.writeNewObject(ObjectOutputStream.java:1404)
01-20 21:05:16.693: E/AndroidRuntime(27035):    at java.io.ObjectOutputStream.writeObjectInternal(ObjectOutputStream.java:1671)
01-20 21:05:16.693: E/AndroidRuntime(27035):    at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:1517)
01-20 21:05:16.693: E/AndroidRuntime(27035):    at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:1481)
01-20 21:05:16.693: E/AndroidRuntime(27035):    at java.io.ObjectOutputStream.writeFieldValues(ObjectOutputStream.java:979)
01-20 21:05:16.693: E/AndroidRuntime(27035):    at java.io.ObjectOutputStream.defaultWriteObject(ObjectOutputStream.java:368)
01-20 21:05:16.693: E/AndroidRuntime(27035):    at java.io.ObjectOutputStream.writeHierarchy(ObjectOutputStream.java:1074)
01-20 21:05:16.693: E/AndroidRuntime(27035):    at java.io.ObjectOutputStream.writeNewObject(ObjectOutputStream.java:1404)
01-20 21:05:16.693: E/AndroidRuntime(27035):    at java.io.ObjectOutputStream.writeObjectInternal(ObjectOutputStream.java:1671)
01-20 21:05:16.693: E/AndroidRuntime(27035):    at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:1517)
01-20 21:05:16.693: E/AndroidRuntime(27035):    at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:1481)
01-20 21:05:16.693: E/AndroidRuntime(27035):    at android.os.Parcel.writeSerializable(Parcel.java:1176)
01-20 21:05:16.693: E/AndroidRuntime(27035):    ... 23 more

Someone correct me if I am wrong, but I would not recommend using Serialization for saving data. 如果我错了,有人会纠正我,但我不建议使用序列化来保存数据。 Since any change to the class will render any previously saved versions of the data unusable. 由于对类的任何更改都将使以前保存的任何数据版本无法使用。 You should consider using a independent format for structuring the data, for example JSON or XML, or a custom one. 您应该考虑使用独立格式来构造数据,例如JSON或XML,或自定义格式。

Using JSON is very easy and maps very easily to (and from) Java objects for example, and can easily be saved as a String Preference in the App. 使用JSON非常简单,并且可以非常容易地映射到(和来自)Java对象,并且可以轻松地在应用程序中保存为字符串首选项。 And you can also handle any changes to the Data pojo that holds the saved state in case you want to add or remove stuff in the future. 您还可以处理保存已保存状态的Data pojo的任何更改,以防您以后要添加或删除内容。

让OnTurnEndedListener扩展Serializable

the application returns a NoSerializableException from the class Game (main activity). 应用程序从类Game(主要活动)返回NoSerializableException。

No. It throws a NotSerializableException mentioning the class Game$6, which is an anonymous instance of OnTurnEndedListener, that you created in the line starting NotSerializableException 。它抛出一个NotSerializableException提到类Game $ 6,它是OnTurnEndedListener,的匿名实例,你在行开始创建

gameData.getTurn().addOnTurnEndedListener(...)

which doesn't extend Serializable . 它没有扩展Serializable So either you have to fix that or make the 所以要么你必须解决这个问题或者做出决定

private ArrayList<OnTurnEndedListener<T>> turnEndListenerList;

into a transient variable, whichever it is that you want. 变成一个transient变量,无论你想要什么。

Your Turn interface has a ArrayList and a ArrayList. 您的Turn界面有一个ArrayList和一个ArrayList。 If either of them are not serializable you will get this error. 如果其中任何一个不可序列化,您将收到此错误。 The fact that T is decided at runtime creates another debugging challenge. 在运行时确定T的事实会产生另一个debugging挑战。 Although its a good design, I recommend that you javadoc it so that developers to use your Turn interface know that the T has to be Serializable too, else it can break during runtime. 虽然它是一个很好的设计,但我建议你javadoc它,以便开发人员使用你的Turn接口知道T必须也是Serializable,否则它可能在运行时中断。

In your case class Hand is not Serializable in most probability. 在您的案例类中, Hand最不可能是Serializable。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM