6

I've this cards game, in which I store all the players in a List. To find out who's the player I want to act with, each player has a Card (I can get the card name), a name (I can get the player's name), but to be unique each player has an ID.

Now, at the beginning of my onCreate() method, I find, and assign a player of the list to a Player, Player clairvoyant:

public void initializeCharacters() {
    for (Player player : players) {
        if (player.getCardName().equals("Clairvoyant")) {
            clairvoyant = player;
        }
}

The game switch between Night and Day. The Clairvoyant's turn is during the night, and I used a switch to determine when's who's turn.

Now, before starting the Clairvoyant's turn, I check if he's alive or not, in negative case, I simply skip his turn:

case 2:
                    clairvoyant(); // Clairvoyant's turn
                    Toast.makeText(Game.this, String.valueOf(clairvoyant.getLifeStatus()), Toast.LENGTH_SHORT).show();
                    if(clairvoyant.getLifeStatus()) {
                    /* --- Let him choose ---*/
                        Intent intent = new Intent(this, ListPlayersClairvoyant.class);
                        Bundle bundle = new Bundle();
                        bundle.putSerializable("PLAYERS", (Serializable) players);
                        intent.putExtras(bundle);
                        startActivityForResult(intent, REQUEST_CLAIRVOYANT);
                    /* --------------------- */
                    }
                    nightInsideCounter++;
                    if(medium == null) {
                        nightInsideCounter++;
                    }
                    if(guard == null) {
                        nightInsideCounter++;
                    }
                    break;

And yet, I've added Toasts to see when I kill players if they're still alive, but even if the Player searched in the list is killed, the Clairvoyant player, previously created isn't.
So basically, the player in the list with the clairvoyant card, is dead; but the clairvoyant player, initialized before to have a reference to it before starting his turn, is still alive!

I don't understand why. Is anything I'm missing? Is that I've done a reference or not? In this case, how should I create a reference to it?


EDIT:

Now it all works fine, I have implemented the Parcelable interface on the Player Class and on the Card one too. The problem is when I get to the intent from ListPlayersVote Activity to the Game one (the main one), the App crashes and I get this error:

java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String java.lang.Object.toString()' on a null object reference at android.widget.ArrayAdapter.createViewFromResource(ArrayAdapter.java:401)

[...] // Many other links, skipped

Which is a simple NullPointer exception, but I get no links to my code for the error, instead I get all other links to other scripts (Probably already made, not by me) and this keeps me stuck, how am I suppose to fix the bug if I don't get any link to my code?


Resources

Vote Activity

Player Class

How I retrieve the ArrayLists passed, and check for their values (this is a StartActivityForResult() ):

    /* ------------------------------------------ */
    /* ---------- If they want to VOTE ---------- */
    /* ------------------------------------------ */
    if(requestCode == REQUEST_VOTE) {

        if(resultCode == Activity.RESULT_OK) {
            // Get the Player clicked
            ArrayList<Player> highestList = data.getParcelableArrayListExtra("HIGHEST");
            ArrayList<Player> highestList1 = data.getParcelableArrayListExtra("HIGHEST1");
            System.out.println("[5] The players of the first list are " + highestList.size() + ".");
            System.out.println("[6] The players of the second list are " + highestList1.size() + ".");

            // Add the most voted players to a signle List, highest
                highest.addAll(highestList);
                highestList.clear();

                highest.addAll(highestList1);
                highestList1.clear();

            // Write the names in chat
            write("Il villaggio ha i propri sospettati:");
            for (Player player : highest){
                write(player.getName());
            }
            System.out.println("[7] The players chosen are " + highest.size() + ".");
            highest.clear();

        } else if (resultCode == Activity.RESULT_CANCELED) {
            // Some stuff that will happen if there's no result
        }

    }
FET
  • 942
  • 4
  • 13
  • 35
  • 6
    *I don't understand why. Is anything I'm missing?* (Serializable) ... player recreated from serializable it is not the same intance as in prev Activity – Selvin Jun 20 '16 at 14:14
  • 2
    Mmm may I ask you further details? @Selvin – FET Jun 20 '16 at 14:15
  • I don't know what you want ... do not use Activity or replace player instance with instance returned from second activity – Selvin Jun 20 '16 at 14:18
  • is there any threading involved? If so, things like proper use of the `volatile` keyword would help. Selvin also is on track for another possible problem -- you may be accidentally instantiating many different `clarivoyant` objects. One way to prevent the latter is to guarantee that the `Player` instance corresponding to the clarivoyant is a singleton (only one can ever exist). – Del Jun 20 '16 at 14:18
  • Is there another way to do this? @Del – FET Jun 20 '16 at 17:09
  • Edited my question @Del – FET Jun 20 '16 at 18:25
  • by the way, do not mix game logic code with UI and platform specific code. this code is a mess – Display Name Jun 21 '16 at 10:41
  • Where's the UI code here sorry? @SargeBorsch – FET Jun 21 '16 at 10:59
  • @FET Toast for example, and Activity switching – Display Name Jun 21 '16 at 11:00
  • Oh, I use Toasts just to see values directly on the screen instead than on the System.out , it's just something I do to test the app while I'm still coding it. Then sure thing I got still lots of things to learn, I'm a beginner, but in that case that's why I used Toasts @SargeBorsch – FET Jun 21 '16 at 11:03
  • @FET there are not only toasts. btw, usually it's good to implement the game logic separately in a library which is not tied to the platform in any way and can be tested separately (from a console app for example). – Display Name Jun 21 '16 at 11:10
  • Does _arjabbar_'s answer work? – Farbod Salamat-Zadeh Jun 22 '16 at 08:44
  • If I've done it correctly, kinda. Now I have implemented the Parcelable interface for the Player class, and passed it as such in every intent where involved. So I basically have no Syntax errors, but when I run the App, it goes all smooth until I get to the intent from the PlayersVote Activity to the Game one /the main). Basically, I get a Runtime error, but it doesn't link me to any line of MY code, it links just to other "Pre made" scripts which of course I should't even touch. So I'm stuck here and I can't even try to fix the problem. I've update my question with the details @FarbodSalamat- – FET Jun 22 '16 at 13:41
  • @FET So what is the line that it's pointing to - i.e. where is your `NullPointerException` happening? – Farbod Salamat-Zadeh Jun 22 '16 at 17:33
  • Yeah, that's the fact, it doseb't point me back to any line I wrote, just pre made code that I can't touch.. @FarbodSalamat-Zadeh – FET Jun 22 '16 at 17:52
  • I've also tried to restart the computer and it still gives me the error @FarbodSalamat-Zadeh – FET Jun 23 '16 at 07:17
  • @FET See if you are using an `ArrayAdapter` in your code anywhere and check for NPEs around there. Use log messages to print where your variables are null to help you. – Farbod Salamat-Zadeh Jun 23 '16 at 09:57
  • The fact is I didn't get this error before implementing Parcelable.. How should I check for NPEs? I mean, how should I use the log messages? Should I run the App first and let it crash? @FarbodSalamat-Zadeh – FET Jun 23 '16 at 10:19
  • @FET Put log messages in certain areas to display if your variable is `null`, so you can find where it became `null`. One issue I had previously with NPEs and implementing `Parcelable` is that my object took `Context` into its parameter (and then in a member variable), but it was lost when I passed the object with `Parcelable` - perhaps that's causing your issue too. – Farbod Salamat-Zadeh Jun 23 '16 at 10:44
  • Mmm You mean I should use System.out.print to make such logs about my variables? @FarbodSalamat-Zadeh And how did you solve your `context` issue? – FET Jun 23 '16 at 10:45
  • @FarbodSalamat-Zadeh I've put some System.out.println() for the ArrayLists I passed through the intents to the Vote Activity and then way back to the Game one. But they're fine. Although, I get this Adapter error, and I don't understand what the error is. I've updated my question with my whole Vote Activity so you can see if I've done something wrong while implementing the RecyclerView, and my Player class too so you can see if I've implemented Parcelable right as well. So frustrating.. thanks for your help! – FET Jun 25 '16 at 13:48
  • *...how am I suppose to fix the bug...* - are you using an ArrayAdapter somewhere in your code? If yes, then check the list of data you are using with it as you have a null value in that list of data. – user Jul 04 '16 at 06:53
  • @Luksprog I already checked and the app crashes when I put in the intent highestList and highestList1, but I printed their values after the intent and they are NOT null, thus, when I bound them together in a single list, that's not NULL neither. But the App crashes. The issue though came up when I implemented Parcelable to Player class, maybe I did something wrong? I posted my Player Class code at the bottom of my question, shall you take a look? – FET Jul 04 '16 at 07:23
  • You didn't answered if you do use an ArrayAdapter somewhere in your code as this is important(as the exception is specifically about an ArrayAdapter), if you do please add some code. In the activity where you actually get the lists returned by the voting activity you should check that they have valid values. – user Jul 04 '16 at 07:34
  • Added the piece of code interested @Luksprog – FET Jul 04 '16 at 07:44
  • And yes, I changed all my `List`s in `ArrayAdapter`s 'cause I was having trouble with those, shall I get back to `List`s? – FET Jul 04 '16 at 07:45
  • I don't see anything wrong in the code you posted. You should debug the code and place breakpoints along the code using the lists, especially *right before* you use them in an ArrayAdapter. You're most likely facing a issue like in this question http://stackoverflow.com/questions/33199631/java-lang-nullpointerexception-attempt-to-invoke-virtual-method-java-lang-stri as I already said in the comment above. – user Jul 04 '16 at 08:12
  • Hey @Luksprog! I've tested it and I've finally found out what's wrong, or kinda: when I pass the lists from the VoteActivity to the Game one, I keep the size of it, but not the.. informations. In fact, those players have no name or other Playerss parameters. So, what's wrong with my Parcelable method? Thank you – FET Jul 08 '16 at 11:14
  • As I said I don't see anything wrong with your implementation of Parcelable. The empty Player objects are present in the GameActivity when you get them in the onActivityResult() callback **or** are they are empty already in the VotingActivity **before** sending them through the Intent? – user Jul 08 '16 at 12:42
  • @Luksprog Just tested: the null list is the highestList1, even before the intent, so in the Voting Class! – FET Jul 08 '16 at 13:58
  • @FET I'm assuming the first Player object in highestList1 is empty, right? I've looked at the code but I don't fully understand the logic you tried implementing there. From what I understood, I wrote some changes to try eliminating the empty Player object. https://gist.github.com/luksprog/c8830c08638bed361cbea969e50aec76 try the changes and see how it goes. – user Jul 08 '16 at 14:21
  • @Luksprog dud you awesome! It works now, I'll try to fix some few bugs here and there to make it smoother and then I'll let you know if everything still works, but for now, please post an answer, bounty is yours! :) – FET Jul 08 '16 at 15:04

3 Answers3

3

It seems you are in search of a weak reference: if no other reference to the object exists, the weak reference is lost too. See Reference too.

WeakReference<Player> clairvoyant;

// Initialisation of clairvoyant from players:
// So do this whenever players is filled.
for (Player player : players) {
    if (player.getCardName().equals("Clairvoyant")) {
        clairvoyant = new WeakReference<>(player);
    }
}

// Everywhere where clairvoyant is inspected:
Player player = clairvoyant.get();
if (player != null) {
    // There is a clairvoyant player:
    ,,,
}

No experience with Android's java behaviour though.

Joop Eggen
  • 107,315
  • 7
  • 83
  • 138
  • Mmm so what should I do? Adding that `WeakReference` for the clairvoyant player? – FET Jul 04 '16 at 07:04
  • Yes, and at the place of using the old `clearvoyant` one checks `clearvoyant.get()` not having become null. – Joop Eggen Jul 04 '16 at 08:34
  • Mmmm I don't know how I should implement it, may you post a snippet maybe? – FET Jul 04 '16 at 14:44
  • Elaborated a bit. As soon as the clairvoyant disappears from the list, the get() becomes null. – Joop Eggen Jul 04 '16 at 16:46
  • Mmm when I edit my Player clairvoyant into a WeakReference, all the methods called on him get lost. I basically need this reference to this Player as it's one of the specials and I have to test if he exists or not at the beginning of my code, but yeah I need to get a reference to him somwhow, and it didn't work using Serializable, that's why they suggested me to use Parcelable, but now I get this error. – FET Jul 05 '16 at 10:51
  • So, should I add another Player using WeakReference or subsitute him with the existing one? And should I add the initializing code to create this reference and then the other to check if it exists (direclty at the top of my other code when I need to make this check)? – FET Jul 05 '16 at 10:52
  • Substiture, so when the clairvoyant player object no longer exists (in the list), it also will disappear from the weak reference. – Joop Eggen Jul 05 '16 at 11:17
  • And how is this gonna fix the issue? – FET Jul 05 '16 at 12:34
  • My assumption was that the code kept the clairvoyant around after it actually should have been dead, garbage collected. For that a WeakReference _can_ be utilised. Probably I misunderstood. – Joop Eggen Jul 05 '16 at 12:46
  • Oh no, I'm sorry, probably that was me, I'll try to explain it clearer now: I need to find inside a List, a Player (Clairvoyant) and once found it (checking if the Name property of the Player class is equal) I need to create a reference to it, so I created a Player Clairvoyant, and stored the player found in there. But I get errors and it looks like using Serializable, it didn't work, so they suggested me to switch to Parcelable, but now I get this error, so my question is either, how do I solve this issue OR, how do I get that reference as it's not working anyway? – FET Jul 06 '16 at 16:31
  • @JoopEggen Let me know what you think – FET Jul 07 '16 at 21:10
  • As I am not doing regular Android programming, I think you are better served by others' help. As the list cannot be long, a dynamic lookup might not be an issue. – Joop Eggen Jul 08 '16 at 06:21
1

Once you serialize and deserialize an object, you won't have the same reference to it as you did before. You need to find another way of storing the player in memory or use a database to store and retrieve information about the player.

Edit:

Basically you have 3 options depending on your application's needs.

  1. You need to pass an object to an Activity in the same application. There is no need to persist the edits to the data between application restarts. Use a straight POJO and store the objects in that class. Make it a singleton that way when the new activity gets an instance of that class, it'll be using the same reference as the other activities.
  2. You need to pass an object between applications, or your edits need to persist between application restarts. Make your objects Parceable. Here there is a good example for implementing that.
  3. Persist you models to a database. Every edit you make to the object needs to be saved in the database and references to the object needs to be passed by ID. Then the receiving activity can pull that record from the database with that ID and it will have the most up to date info on it.

And as far as your NullPointerException. I assume that getID() returns a Integer type which is nullable. Whenever those primitive class wrappers are used in expressions they can throw a NullPointerException when unboxing. Always check for null when using those classes like Integer or Boolean.

Community
  • 1
  • 1
arjabbar
  • 6,044
  • 4
  • 30
  • 46
  • Oh, well, any other way to store that reference to the clairvoyant player? – FET Jun 20 '16 at 17:08
  • Updated my question – FET Jun 20 '16 at 18:25
  • Updated my answer. – arjabbar Jun 20 '16 at 19:29
  • Not just checking for it. You just have to handle it by returning out of the method or not dereferencing it. It seems like you can't do anything if `playerChose.getId()` is null. First check that and return out of the method or log it if that's null. Then you can do `playerChose.getId().equals(/* another ID */)` to perform your other checks. – arjabbar Jun 20 '16 at 19:50
  • But I don't understand why it's null, it never happened though all my tests, wil now. Would implementing Parcelable solve the other issue and this one as well? – FET Jun 20 '16 at 20:00
  • Oh, and I'm trying to implement Parcelable on my Class, but how do I write and read back a boolean type and a custom class of mine? – FET Jun 20 '16 at 20:00
  • Honestly I would have to dig more into application to be able to tell whether or not implementing Parcelable would fix anything. And how to read and write custom values in a Parcelable might be another question that you have to post on here or something else that you have to research. I would start with that example I posted. You may have to have your custom class implement Parcelable as wel.. – arjabbar Jun 20 '16 at 20:05
  • What should I do with the App then? What do you suggest? Ya need the code? (For the Parcelable thing, I'll make another question) – FET Jun 20 '16 at 20:10
1

The problem is when I get to the intent from ListPlayersVote Activity to the Game one (the main one), the App crashes and I get this error:

As I said in my comment, that exception related to the ArrayAdapter class happens when the adapter is used with a list of data that has null values in it. Looking through your code the culprit seems to be a Player object that doesn't have its fields initialized in the ListPlayersVote class. In that class, your current code will almost always introduce that non initialized Player object reference. In the ListPlayersVote's onCreate() callback you first add two Player objects to the holder lists, I'm assuming to have as a base for further calculations:

// remove the lines below , there's no need for them
// also simply doing new Player() will result in a Player which doesn't have    
// its fields initialized(and you don't update them later either)
highestList.add(new Player()); // is there a reason for adding this empty players?
highestList1.add(new Player());

Then in the onEntryClick() callback update your code to properly handle counting the players votes:

for (Player player1 : players){ // For each player
  // first of all you need to move the initialization for highest and highest1 INSIDE the
  // for loop to use the proper highest values at each iteration
  // With your current code the lists highestlist and highestlis1 each have
  // ONE Player object which is empty (because in the default constructor 
  // of Player you do nothing), you then use the empty initial Player in each list to
  // compare the vote count, this will mostly result in the first if  
  // clause(playerCount > highest) being triggered,
  // which will add the initial empty Player object to highestlist1
  int highest = highestList.size() == 0 ? -1 : highestList.get(0).getCount();// if the list is empty initialize it with -1 to signal the first player handled
  int highest1 = highestList1.size() == 0 ? -1 : highestList1.get(0).getCount();

  int playerCount = player1.getCount(); // Get his votes

    // you need to check for -1 to handle the case when you're dealing 
    // with the first player( which of course will have the highest 
    // count as there's no either player to compare it yet)
    if(playerCount > highest || highest == -1){ // If they're the highest so far, add it to the 1st list
        highestList1.addAll(highestList);
        highestList.clear();
        highestList.add(player1);
        // the same for highest1
    } else if (playerCount > highest1 || highest1 == -1){ // If they're the 2nd highest so far, add it to the 2nd list
        highestList1.clear();
        highestList1.add(player1);
    }else if(playerCount == highest){ // If they're equal to the current highest one, add it to the 1st list as well
        highestList1.add(player1);
    } else if (playerCount == highest1){ // If they're equal to the current 2nd highest one, add it to the 2nd list as well
        highestList1.add(player1);
    }
Zero Fiber
  • 4,417
  • 2
  • 23
  • 34
user
  • 86,916
  • 18
  • 197
  • 190
  • Hey Luk! I have implemented SharedPreferences on the app so that now we can kill it and open back our last activity used. But it seems like the mechanism to handle the votes for the players is kinda broken.. ir accpets 3 players to instead of 2 only.. Please if you will to help this is the [question I'm referring to](http://stackoverflow.com/questions/38307500/how-to-save-data-and-re-open-the-last-used-activity), and this is my [code on Git](https://github.com/SnipCode/Scurcola_1.0) so you can test it on your own and see it in action (I've added some logs on the System.out to help) – FET Jul 19 '16 at 08:42
  • Oh and this is the chat I'm having with another guy who helped fix that question problem! Make sure to check it out to stay up to date! http://chat.stackoverflow.com/rooms/117453/discussion-between-fet-and-f43nd1r – FET Jul 19 '16 at 08:47
  • @FET I've looked over your code but I don't see the problem and I don't have any time to look very thorough. Your best bet is to debug the application and step over the code, as you need to actually know how the game is played. Also, as the other user mentioned you've approached the application incorrectly with all those activities, you should have used fragments which would have made your app much easier to build and debug. – user Jul 20 '16 at 04:00
  • 1
    True, at the end though I've found the bug, there were missing 2 additional if statements, thanks tho man :) – FET Jul 20 '16 at 07:56