1

当我尝试在我的活动中运行 Andriod JUnit 测试时出现错误:

Test run failed: Instrumentation run failed due to 'java.lang.NullPointerException'

05-31 15:57:19.159: E/AndroidRuntime(22342): FATAL EXCEPTION: main
05-31 15:57:19.159: E/AndroidRuntime(22342): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.wecharades/com.example.wecharades.views.GameDashboardActivity}: java.lang.NullPointerException
05-31 15:57:19.159: E/AndroidRuntime(22342):    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1967)
05-31 15:57:19.159: E/AndroidRuntime(22342):    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1992)
05-31 15:57:19.159: E/AndroidRuntime(22342):    at android.app.ActivityThread.access$600(ActivityThread.java:127)
05-31 15:57:19.159: E/AndroidRuntime(22342):    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1158)
05-31 15:57:19.159: E/AndroidRuntime(22342):    at android.os.Handler.dispatchMessage(Handler.java:99)
05-31 15:57:19.159: E/AndroidRuntime(22342):    at android.os.Looper.loop(Looper.java:137)
05-31 15:57:19.159: E/AndroidRuntime(22342):    at android.app.ActivityThread.main(ActivityThread.java:4441)
05-31 15:57:19.159: E/AndroidRuntime(22342):    at java.lang.reflect.Method.invokeNative(Native Method)
05-31 15:57:19.159: E/AndroidRuntime(22342):    at java.lang.reflect.Method.invoke(Method.java:511)
05-31 15:57:19.159: E/AndroidRuntime(22342):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784)
05-31 15:57:19.159: E/AndroidRuntime(22342):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551)
05-31 15:57:19.159: E/AndroidRuntime(22342):    at dalvik.system.NativeStart.main(Native Method)
05-31 15:57:19.159: E/AndroidRuntime(22342): Caused by: java.lang.NullPointerException
05-31 15:57:19.159: E/AndroidRuntime(22342):    at java.util.TreeMap$1.compare(TreeMap.java:72)
05-31 15:57:19.159: E/AndroidRuntime(22342):    at java.util.TreeMap$1.compare(TreeMap.java:70)
05-31 15:57:19.159: E/AndroidRuntime(22342):    at java.util.TreeMap.find(TreeMap.java:277)
05-31 15:57:19.159: E/AndroidRuntime(22342):    at java.util.TreeMap.findByObject(TreeMap.java:351)
05-31 15:57:19.159: E/AndroidRuntime(22342):    at java.util.TreeMap.get(TreeMap.java:177)
05-31 15:57:19.159: E/AndroidRuntime(22342):    at com.example.wecharades.model.Model.getTurns(Model.java:258)
05-31 15:57:19.159: E/AndroidRuntime(22342):    at com.example.wecharades.model.DataController.getTurns(DataController.java:460)
05-31 15:57:19.159: E/AndroidRuntime(22342):    at com.example.wecharades.presenter.GameDashboardPresenter.createDashboard(GameDashboardPresenter.java:53)
05-31 15:57:19.159: E/AndroidRuntime(22342):    at com.example.wecharades.views.GameDashboardActivity.onStart(GameDashboardActivity.java:48)
05-31 15:57:19.159: E/AndroidRuntime(22342):    at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1133)
05-31 15:57:19.159: E/AndroidRuntime(22342):    at android.app.Activity.performStart(Activity.java:4475)
05-31 15:57:19.159: E/AndroidRuntime(22342):    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1940)
05-31 15:57:19.159: E/AndroidRuntime(22342):    ... 11 more

问题是我的一些测试得到了这个错误信息,而一些工作正常。它们都链接到同一个项目,所有测试都使用 ActivityInstrumentationTestCase2。即使我刚刚获得了一些测试的框架并且所有测试都基于相同的代码,但其中一些只是无法工作并不断出现错误。

这是一个非常简单的代码,它得到了这个错误:

public class GameDashboardActivityTest extends ActivityInstrumentationTestCase2<GameDashboardActivity> {

    public GameDashboardActivityTest() {
        super(GameDashboardActivity.class);
    }
    private GameDashboardActivity activity;

    protected void setUp() throws Exception {
        super.setUp();
        activity = getActivity();
        assertNotNull(activity);
    }

    /**
     * Run the void updateScore(int currentPlayersScore, int otherPlayerScore) method test.
     * @throws Throwable 
     *
     */
    public void testUpdateScore_1()
            throws Throwable {
        assertTrue(true);
    }

    protected void tearDown() throws Exception {
        super.tearDown();
    }

}

GameDashboardActivity

package com.example.wecharades.views;

import android.os.Bundle;
import android.view.View;
import android.view.Window;
import android.widget.ImageButton;
import android.widget.TableLayout;
import android.widget.TextView;

import com.example.wecharades.R;
import com.example.wecharades.presenter.GameDashboardPresenter;

/**
 * View which displays the game dashboard
 * @author weCharade
 */
public class GameDashboardActivity extends GenericActivity {

    private TableLayout myTable;
    private TextView title;
    private TextView yourScore;
    private TextView opponentsScore;
    private GameDashboardPresenter presenter;

    @Override
    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState, new GameDashboardPresenter(this));

        //Set the title bar
        requestWindowFeature(Window.FEATURE_CUSTOM_TITLE);
        setContentView(R.layout.game_screen);
        getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.title_bar_home); 

        //Get references to instances
        presenter = (GameDashboardPresenter) super.getPresenter();
        title = (TextView) findViewById(R.id.titleText);
        yourScore = (TextView) findViewById(R.id.yourScore);
        opponentsScore = (TextView) findViewById(R.id.opponentScore);
        myTable = (TableLayout) findViewById(R.id.table);

    }

    @Override
    public void onStart(){

        super.onStart();
        presenter.createDashboard(myTable);

    }

    /**
     * Updates both players' scores
     * @param currentPlayersScore
     * @param otherPlayerScore
     */
    public void updateScore(int currentPlayersScore, int otherPlayerScore) {

        yourScore.setText(Integer.toString(currentPlayersScore));
        opponentsScore.setText(Integer.toString(otherPlayerScore));

    }

    /**
     * Set title of the Game dashboard
     * @param title
     */
    public void setTitle(String title) {

        this.title.setText(title);

    }

    /**
     * Go to StartActivity
     * @param v
     */
    public void onClickHome(View v){

        presenter.goToStartActivity();

    }

    @Override
    protected RefreshProgressBar getProgressBar() {
        return null;
    }
}


Model.java    
    /**
     * This class stores all the data available in the game locally.
     *  This class exist to reduce the number or request to parse.com
     *
     */
public class Model implements Serializable{

    private static final long serialVersionUID = -8167671678222883966L;
    //The name of our model save file
    private static final String     SAVE_FILE = "model.save";
    //Stored variables to use in other classes - should maybe be in another class.
    public static final int         
    FINISHEDGAMES_SAVETIME          = 168
    , FINISHEDGAMES_NUMBERSAVED     = 10
    , INVITATIONS_SAVETIME          = 72;

    /*
     * A variable that can be changed in order to purge the model - this is done manually when needed!
     *  When this is set to true, the model will be forced to be recreated. This is done to purge the
     *  Model and retrieve a mirror of the database information, while preserving login status 
     *  (and avoid having to reinstall and log in and out again). This MIGHT be implemented as a feature later. 
     * 
     *  -- DO NOT FORGET TO RESET THIS AFTERWARDS! --
     */
    private static boolean          PURGE = false;

    //A variable to check if model is already saved.
    private boolean                 SAVED = false;
    //A variable which is called when a user logs out 
    // - the model exists a moment so we may finish any queries first
    private static boolean          RECREATE = false;

    //Two maps for games for increased speed and ease of use
    private TreeMap<Game, ArrayList<Turn>> gameList = new TreeMap<Game, ArrayList<Turn>>();
    private TreeMap<String, Game> gameIdList = new TreeMap<String, Game>();

    //Two maps for player names and id:s. The second one is used for increased speed and ease of use
    private TreeMap<String, Player> storedPlayers = new TreeMap<String, Player>();
    private TreeMap<String, String> storedPlayerNames = new TreeMap<String, String>();
    private Player currentPlayer = null;

    // Invitations are stored locally in two lists
    private LinkedList<Invitation> sentInvitations = new LinkedList<Invitation>();
    private LinkedList<Invitation> receiveInvitations = new LinkedList<Invitation>();

    //Singleton
    private static Model singleModel;

    private Model(Context context){
        //Creating a file to save to
        if(context != null){
            saveModel(context);
        }
    }

    /**
     * Use this method to get the singleton instance of the model where necessary.
     * @return the Model
     */
    public static Model getModelInstance(Context context){
        if(PURGE){
            //If the PURGE variable is set to true (done manually), the model will be recreated
            eraseModel(context);
            singleModel = null;
            PURGE = false;
        }
        if(singleModel == null){
            //Try to load from storage
            singleModel = loadModel(context);
        }
        if(singleModel == null || RECREATE){
            //If there were no previous models present, create a new one
            singleModel = new Model(context);
            RECREATE = false;
        }
        return singleModel;
    }

    /**
     * A method to save the current model to memory.
     * @param context - used to retrieve a save location
     */
    public void saveModel(Context context){
        if(!SAVED && context != null){
            try {
                FileOutputStream ops = context.openFileOutput(SAVE_FILE, Context.MODE_PRIVATE);
                ObjectOutputStream oOut = new ObjectOutputStream(ops);
                oOut.writeObject(singleModel);
                oOut.close();
                SAVED = true;
            } catch (IOException e) {
                Log.d("IO - Model save", e.getMessage());
            }
        }
    }

    /**
     * Method to load a model form memory
     * @param context
     * @return
     */
    private static Model loadModel(Context context){
        Model singleModel = null;
        if(context != null){
            try {
                ObjectInputStream oIn = new ObjectInputStream(context.openFileInput(SAVE_FILE));
                Object obj = oIn.readObject();
                if (obj != null && obj.getClass().equals(Model.class)){
                    singleModel = (Model) obj;
                }
            } catch (FileNotFoundException e1){
                Log.d("IO - Model load", "No file found");
            } catch (IOException e2){
                Log.d("IO - Model load", "IOException");
            } catch (ClassNotFoundException e3){
                Log.d("IO - Model load", "ClassNotFound");
            }
        }
        return singleModel;
    }

    /**
     * Called to erase the current model from memory and disk.
     * @param context
     */
    private static void eraseModel(Context context){
        if(context != null){
            File modelFile = new File(context.getFilesDir(), SAVE_FILE);
            if(modelFile.delete()){
                Log.d("Model - File:","Removed file");
            }
            RECREATE = true;
        }
    }

    //Games ---------------------------------------------------------------

    /**
     * Updates a list of games. If a game is not existant, it will be added to the list. 
     * @param games
     */
    public void putGameList(ArrayList<Game> games){
        for(Game game : games){
            putGame(game);
        }
        SAVED = false;
    }

    /**
     * Updates a game in the internal list of games. Will also create new games that does not exist.
     * @param game - the game to be updated
     */
    public void putGame(Game game){
        //This is actually kind of fast, although it might look a bit weird.
        ArrayList<Turn> tempTurns;
        if(gameList.containsKey(game) && gameList.get(game) != null){
            tempTurns = gameList.get(game);
            gameList.remove(game);
            gameList.put(game,tempTurns);
            gameIdList.put(game.getGameId(), game);
        } else{
            gameList.put(game, null);
            gameIdList.put(game.getGameId(), game);
        }
        SAVED = false;
    }

    /**
     * Return an ArrayList with current games
     * @return - an arraylist containing games
     */
    public ArrayList<Game> getGames(){
        return new ArrayList<Game>(gameList.keySet());
    }

    /**
     * Gets a game from its game id
     * @param parseId
     * @return a Game, or null it does not exist
     */
    public Game getGame(String parseId){
        return gameIdList.get(parseId);
    }

    /**
     * Removes a game form the model
     * @param game - the game to be deleted
     * @return - true if the game was in the list, false otherwise
     */
    public void removeGame(Game game){
        gameIdList.remove(game.getGameId());
        gameList.remove(game);
        SAVED = false;
    }

    /**
     * Use to update a single turn of a game. This will add a turn if it does not exist,
     *  as well as update its state if it is existant.
     * @param game - the game in question
     * @param turn - the turn of the game
     * @throws NoSuchElementException if no game is found
     */
    public void putTurn(Turn turn){
        if(turn != null){
            if(!gameIdList.containsKey(turn.getGameId()))
                throw new NoSuchElementException();
            Game game = getGame(turn.getGameId());
            ArrayList<Turn> listOfTurns = gameList.get(game);
            if(listOfTurns == null){
                listOfTurns = new ArrayList<Turn>();
                gameList.put(game, listOfTurns);
            } else if(listOfTurns.contains(turn)){
                //If the turn contains the turn, we must delete it first
                listOfTurns.remove(turn);
            }
            listOfTurns.add(turn);
        }
        SAVED = false;
    }

    /**
     * Updates a list of turns at once - the existing list will be overwritten.
     * @param turnList
     * @throws NoSuchElementException if no game is found
     */
    public void putTurns(ArrayList<Turn> turnList) throws NoSuchElementException{
        //Do not simply replace the list, as this might cause problems with the amount of turns etc.
        for(Turn turn : turnList){
            putTurn(turn);
        }
    }

    /**
     * Get a list of turns for a game
     * @param game - the game
     * @return - an arraylist of turns
     */
    public ArrayList<Turn> getTurns(Game game){
        return gameList.get(game);
    }

    /**
     * Returns the current turn from the model
     * @param game - the game to fetch from
     * @return a Turn
     */
    public Turn getCurrentTurn(Game game) {
        if(game != null){
            ArrayList<Turn> turns = getTurns(game);
            if(turns != null){
                for(Turn t : turns){
                    //Find the turn with CurrentTurnNumber
                    if(t.getTurnNumber() == game.getTurnNumber()){
                        return t;
                    }
                }
            }
        }
        return null;
    }

    //Players ---------------------------------------------------------------

    /**
     * Puts a player in stored players 
     * @param player - the player to be stored
     */
    public void putPlayer(Player player){
        //The data for a player should always be updated
        storedPlayerNames.put(player.getName(), player.getParseId());
        storedPlayers.put(player.getParseId(),player);
        SAVED = false;
    }

    /**
     * Puts a collection of players into the model
     * @param players - a collection of players
     */
    public void putPlayers(Collection<Player> players){
        storedPlayers.clear();
        storedPlayerNames.clear();
        for(Player player : players){
            putPlayer(player);
        }
    }

    /**
     * Used to get a player representation from a username
     * @param username - the player username
     * @return a Player, or null if no player was found
     */
    public Player getPlayer(String username){
        Player retPlayer = null;
        if(storedPlayerNames.containsKey(username)){
            retPlayer = storedPlayers.get(storedPlayerNames.get(username));
        }
        return retPlayer;
    }

    /**
     * Used to get a player representation from a username
     * @param parseId - the player id
     * @return a Player or null if not found
     */
    public Player getPlayerById(String parseId){
        return storedPlayers.get(parseId);
    }

    /**
     * Designates a player as the current player. If the player does not exist in cache,  
     *  it gets added.
     */
    public void setCurrentPlayer(Player player){
        currentPlayer = player;
        putPlayer(player);
        SAVED = false;
    }

    /**
     * Returns the logged in player player (ParseUser)
     * @return A Player representation of The current player, or null if this player does not exist.
     */
    public Player getCurrentPlayer(){
        return currentPlayer;
    }

    /**
     * Deletes the current player entirely from the model. Should be done when user logs out.
     */
    public void logOutCurrentPlayer(Context context){
        eraseModel(context);
    }

    //Invitations ---------------------------------------------------------------
    //Received invitations are not needed here, as they should allways be fetched from the database.

    /**
     * Set all sent invitations from this player. This replaces the local version of this game.
     * @param invitations - The invitations to add
     */
    public void setSentInvitations(LinkedList<Invitation> invitations){
        if(invitations != null){
            sentInvitations = invitations;
        } else{
            sentInvitations.clear();
        }
        SAVED = false;
    }

    /**
     * Set all received invitations to this player
     * @param invitations - The invitations to add
     */
    public void setReceivedInvitations(LinkedList<Invitation> invitations){
        if(invitations != null){
            receiveInvitations = invitations;
        } else{
            receiveInvitations.clear();
        }
        SAVED = false;
    }

    /**
     * Retrieve a list of Invitations sent from this device.
     * @return A List containing invitations.
     */
    public List<Invitation> getSentInvitations(){
        return sentInvitations;
    }

    /**
     * Retrieve a list of Invitations the current player has received
     * @return A List containing invitations.
     */
    public List<Invitation> getReceivedInvitations(){
        return receiveInvitations;
    }

}

通用活动.java

/**
 * Abstract class which holds implementations of, for the activities, generic methods
 * @author weCharade
 */
public abstract class GenericActivity extends Activity {

    //Presenter object, declared protected and therefore enabling access to extending classes
    protected Presenter presenter;

    /**
     * onCreate-method which sets the presenter
     * @param savedInstanceState
     * @param presenter
     */
    public void onCreate(Bundle savedInstanceState, Presenter presenter) {

        //Only send the Bundle-object to the super class 
        super.onCreate(savedInstanceState);
        this.presenter = presenter;
    }

    /**
     * This method will return a ProgressBar in the form of a spinner.
     * Use this spinner to give a visual queue to the user that something is happening
     * in the background. 
     * @return The progressbar of the view.
     */
    protected abstract IProgress getProgressBar();


    /**
     * Called to show progress spinning when waiting for the server
     */
    public void showProgressBar() {
        if(getProgressBar() != null) {
            getProgressBar().show();
        }
    }

    /**
     * Called to hide progress spinning when the server has responded
     */
    public void hideProgressBar() {
        if(getProgressBar() != null) {
            getProgressBar().hide();
        }
    }

    /**
     * Private help method to get all clickable objects in a list from a view.
     * @param view
     * @return an ArrayList with all Views within the parameter view
     */
    private ArrayList<View> getAllChildren(View view) {

        //Check if the view is a "single" view
        if (!(view instanceof ViewGroup)) {
            ArrayList<View> viewArrayList = new ArrayList<View>();
            viewArrayList.add(view);
            return viewArrayList;
        }

        ArrayList<View> result = new ArrayList<View>();

        /*  
         * Add all children of the ViewGroup and eventually return the 
         * ArryaList containing the views.Childrens of a children in a ViewGroup are
         * added to the list by recursively calling the method.
         */
        ViewGroup vg = (ViewGroup) view;
        for (int i = 0; i < vg.getChildCount(); i++) {

            View child = vg.getChildAt(i);

            ArrayList<View> viewArrayList = new ArrayList<View>();
            viewArrayList.add(view);
            viewArrayList.addAll(getAllChildren(child));

            result.addAll(viewArrayList);
        }
        return result;
    }

    /**
     * Enable all clickable objects in view.
     * @param view a List containing all elements of the view to enable
     */
    public void enabledView() {
        List<View> viewList = getAllChildren(this.getWindow().getDecorView().findViewById(android.R.id.content));

        //Loop through the list of views and enable them.
        for (View child : viewList) {
            child.setEnabled(true);
        }
    }
    /**
     * Disable or disable all clickable objects in view.
     * @param view a List containing all elements of the view to disable
     */
    public void disableView() {
        List<View> viewList = getAllChildren(this.getWindow().getDecorView().findViewById(android.R.id.content));

        //Loop through the list of views and disable them.
        for (View child : viewList) {
            child.setEnabled(false);
        }
    }

    /**
     * Show a toast to the user.
     * @param msg
     */
    public void showToast(String msg) {

        //Declare and get reference to a LayoutInflater
        LayoutInflater inflater = getLayoutInflater();

        //Inflate custom Toast layout
        View layout = inflater.inflate(R.layout.toast_success, (ViewGroup) findViewById(R.id.toast_layout_root));

        TextView text = (TextView) layout.findViewById(R.id.toastText);
        text.setText(msg);

        //Create Toast, set duration and layout
        Toast toast = new Toast(getApplicationContext());
        toast.setDuration(Toast.LENGTH_LONG);
        toast.setView(layout);
        toast.show();
    }

    /**
     * Show a message to the user, most often an error. Uses a dialog-box with one button.
     * @param error
     */
    public void showNegativeDialog(String negativeTitle, String negativeText, String buttonText) {

        final Dialog dialog = new Dialog(this);

        //Actions to set custom Dialog layout and set properties
        dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
        dialog.setContentView(R.layout.dialog_negative);
        dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));  
        dialog.setCanceledOnTouchOutside(false);        

        TextView title = (TextView) dialog.findViewById(R.id.negativeTitle);
        title.setText(negativeTitle);

        TextView text = (TextView) dialog.findViewById(R.id.negativeText);
        text.setText(negativeText);

        Button button = (Button) dialog.findViewById(R.id.dismiss);
        button.setText(buttonText);
        button.setOnClickListener(new OnClickListener() {          

            @Override
            public void onClick(View v) {
                dialog.dismiss();
            }
        });

        dialog.show();
    }

    /**
     * Show a message to the user, most often an error. Uses a dialog-box with two buttons.
     * @param negativeTitle
     * @param negativeText
     * @param buttonText1
     * @param buttonText2
     */
    public void showNegativeDialog(String negativeTitle, String negativeText, String buttonText1, String buttonText2) {

        //Create new Dialog-object
        final Dialog dialog = new Dialog(this);

        //Actions to set custom Dialog layout and set properties
        dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
        dialog.setContentView(R.layout.dialog_negative_two);
        dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
        dialog.setCanceledOnTouchOutside(false);

        TextView title = (TextView) dialog.findViewById(R.id.negativeTitle);
        title.setText(negativeTitle);

        TextView text = (TextView) dialog.findViewById(R.id.negativeText);
        text.setText(negativeText);

        Button button1 = (Button) dialog.findViewById(R.id.dismiss);
        button1.setText(buttonText1);

        button1.setOnClickListener(new OnClickListener() {          

            @Override
            public void onClick(View v) {
                dialog.dismiss();
            }
        });

        Button button2 = (Button) dialog.findViewById(R.id.back);
        button2.setText(buttonText2);

        button2.setOnClickListener(new OnClickListener() {          

            @Override
            public void onClick(View v) {
                dialog.dismiss();
            }
        });

        dialog.show();
    }

    /**
     * Show a positive dialog to the user. Most often a success message
     * @param error
     */
    public void showPositiveDialog(String positiveTitle, String positiveText, String buttonText) {

        //Create new Dialog-object
        final Dialog dialog = new Dialog(this);

        //Actions to set custom Dialog layout and set properties
        dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
        dialog.setContentView(R.layout.dialog_positive);
        dialog.setCanceledOnTouchOutside(false);
        dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));               

        TextView title = (TextView) dialog.findViewById(R.id.positiveTitle);
        title.setText(positiveTitle);

        TextView text = (TextView) dialog.findViewById(R.id.positiveText);
        text.setText(positiveText);

        Button button = (Button) dialog.findViewById(R.id.dismiss);
        button.setText(buttonText);

        button.setOnClickListener(new OnClickListener(){

            @Override
            public void onClick(View v) {
                dialog.dismiss();

            }
        });
        dialog.show();
    }

    /**
     * Show a progress dialog to the user. 
     * @param error
     */
    public void showProgressDialog(String positiveTitle, String positiveText, String buttonText) {
        //TODO: To be implemented
    }



    @Override
    protected void onStop(){
        //Save the model to disk whenever an activity is closed.
        presenter.saveState();
        super.onStop();
    }

    /**
     * Get the presenter associated with an activity.
     * @return A presenter
     */
    protected Presenter getPresenter(){
        return presenter;
    }

}

NullPointerException 似乎发生在 getActivity() 部分,但我不知道为什么。有任何想法吗?

//费利克斯

4

1 回答 1

0

Java 中的原因链似乎让您感到困惑。您需要深入到最后一个原因才能看到根本原因。在这种情况下,它是:

at com.example.wecharades.model.Model.getTurns(Model.java:258)
于 2013-05-31T14:55:45.953 回答