5

伙计们,我对我正在开发的一个新的 Android 应用程序感到很困惑。对于应用程序(纸牌游戏),我需要保存一些数据。我使用序列化来完成这项工作。

现在的问题是: 当我尝试实现一个用于跟踪付款人轮次的接口时,应用程序从 Game 类(主要活动)返回 NoSerializableException。当我删除界面时,一切正常。

turn 类包含以下代码:

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

}
}

当我在主要活动(游戏)中添加以下代码时,每次关闭活动时都会出现异常。

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

您可以在下面找到 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();
        }
    }

(我从上面的例子中删除了很多代码,因为解决这个问题不需要这些代码)

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

}

日志:

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
4

4 回答 4

2

如果我错了,有人纠正我,但我不建议使用序列化来保存数据。由于对类的任何更改都会导致任何先前保存的数据版本不可用。您应该考虑使用一种独立的格式来构建数据,例如 JSON 或 XML,或者自定义格式。

使用 JSON 非常简单,并且非常容易映射到(和从)Java 对象,例如,可以很容易地保存为应用程序中的字符串首选项。并且您还可以处理对保存状态的 Data pojo 的任何更改,以防您将来要添加或删除内容。

于 2013-01-20T20:56:12.747 回答
1

让 OnTurnEndedListener 扩展 Serializable

于 2013-01-20T21:52:46.533 回答
1

应用程序从 Game 类(主要活动)返回 NoSerializableException。

不,它提到了Game$6 类,它是您在开始的行中创建NotSerializableException的匿名实例OnTurnEndedListener,

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

不扩展Serializable。所以要么你必须解决这个问题,要么让

private ArrayList<OnTurnEndedListener<T>> turnEndListenerList;

成一个transient变量,无论是你想要的。

于 2013-01-21T01:21:27.857 回答
-1

你的 Turn 接口有一个 ArrayList 和一个 ArrayList。如果它们中的任何一个都不可序列化,您将收到此错误。T 在运行时决定的事实带来了另一个debugging挑战。虽然它是一个很好的设计,但我建议您使用 javadoc,以便使用您的 Turn 接口的开发人员知道 T 也必须是可序列化的,否则它可能会在运行时中断。

在您的情况下,类Hand很可能是不可序列化的。

于 2013-01-21T04:27:20.527 回答