1

I've made the majority of my game's mechanics, I now need to be able to:

  1. Save all the data of the current activity and retrieve it when coming back (I'd appreciate an example of SharedPreferences if that's what I need)

  2. Open back the same Activity I left from and in the same time I was when I left it.


Just to be clearer: I want not to restart from my Main Activity each time the App gets closed or even killed.


EDIT:

Alright, I've used this Google article in order to save my Activity and recreate it later.

The code in one of my Activities is the following:

onCreate()

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

    // Check whether we're recreating a previously destroyed instance
    if (savedInstanceState != null) {
        // Restore value of members from saved state
        village = savedInstanceState.getString(VILLAGE);
        seekBarProgress = savedInstanceState.getInt(PROGRESS);
    } else {
        // Probably initialize members with default values for a new instance
        initializeVariables();
        Bundle bundle = getIntent().getExtras();
        village = bundle.getString(VILLAGE);
    }
        [...] // Other code skipped
    }

onSaveInstanceState()

@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
    // Save the user's current game state
    savedInstanceState.putString(VILLAGE, village);
    savedInstanceState.putInt(PROGRESS, seekBarProgress);
    // Always call the superclass so it can save the view hierarchy state
    super.onSaveInstanceState(savedInstanceState);
}

Thus, I've created a Dispatcher Activity (which is now my Main) that when the App is killed and then launched back, knows which is the last opened Activity to launch. But when I try to do that, and it should open (i.e.) the PlayerSelection Activity, I get a

java.lang.NullPointerException

as it runs this part of code

else { // Probably initialize members with default values for a new instance initializeVariables(); Bundle bundle = getIntent().getExtras(); village = bundle.getString(VILLAGE); }

instead of the previous if statement it runs the else.


Dispatcher

This is the Dispatcher Activity which filters which Activity should be launched:

public class Dispatcher extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    Class<?> activityClass;

    try {
        SharedPreferences prefs = getSharedPreferences("X", MODE_PRIVATE);
        activityClass = Class.forName(
                prefs.getString("lastActivity", MainActivity.class.getName()));
    } catch(ClassNotFoundException ex) {
        activityClass = MainActivity.class;
    }

    startActivity(new Intent(this, activityClass));
    }
}

This is what I send to my Dispatcher Activity:

@Override
protected void onPause() {
    super.onPause();

    SharedPreferences prefs = getSharedPreferences("X", MODE_PRIVATE);
    SharedPreferences.Editor editor = prefs.edit();
    editor.putString("lastActivity", getClass().getName());
    editor.commit();
    }
}

Question

Why does it happen? What can I do to fix this?

Community
  • 1
  • 1
FET
  • 942
  • 4
  • 13
  • 35
  • To save/restore your Activity state, see [this](https://developer.android.com/training/basics/activity-lifecycle/recreating.html). – Phantômaxx Jul 11 '16 at 15:33
  • To start another activity... I guess you must save something (i.e.: a SharedPreference) to tell the Main Activity (or the loading Activity) which Activity you want to open. – Phantômaxx Jul 11 '16 at 15:34
  • Like a switch on that received from the SharedPreference? @BobMalooga – FET Jul 11 '16 at 16:52
  • Kinda. A string telling your launcher which Activity to fire. – Phantômaxx Jul 12 '16 at 15:06
  • Mh, not a switch? I mean, shouldn't the intent have something like ActivityToLaunch.class as a parameter? Can it be a String? @BobMalooga – FET Jul 12 '16 at 18:13
  • You can compare the string you have retrieved with some conventional names and, if equal, create an Intent to the corresponding Activity. Then just start that Intent. – Phantômaxx Jul 13 '16 at 15:36
  • That's what I've done trough the Dispatcher Activity @BobMalooga – FET Jul 13 '16 at 16:43

2 Answers2

0

The onSaveInstanceState will not be called when you press back key to finish it. To save your data all the time you have to use onPause as the code shows:

@Override
public void onPause() {
    super.onPause();
    SharedPreferences sp = getSharedPreferences("X", Context.MODE_PRIVATE);
    SharedPreferences.Editor editor = sp.edit();
    editor.putString("lastActivity", HomeActivity.class.getCanonicalName());
}

Remember to replace HomeActivity with your own class name.BTW, I didn't test the function getCanonicalName, but it should work.

In your Dispatch Activity, you read the value back and do some action to it.

suitianshi
  • 3,300
  • 1
  • 17
  • 34
  • Edited my question with the code I used to send the lastActivity value to my Dispatcher Activity. – FET Jul 14 '16 at 10:38
0

Why does it happen?

The Bundle created in onSaveInstanceState is only kept for short-term restarts, e.g. when your activity is recreated due to an orientation change. If your activity stops normally this Bundle is not kept, and thus can't be delivered to onCreate.

What can I do to fix this?

Instead of using the instanceState model, save the activity state in SharedPreferences (similar to the lastActivity which you already save). You should save your state in onStop or onPause and restore it in onStart or onResume respectively. In most cases the former is enough and the latter produces only unnecessary overhead.


@Override
protected void onStart() {
    super.onStart();
    String saved = sharedPreferences.getString("myKey", null);
    MyPojo pojo;
    if(saved == null){
        pojo = defaultValue;
    }else {
        pojo = new Gson().fromJson(saved, MyPojo.class);
    }
}

@Override
protected void onStop() {
    sharedPreferences.edit().putString("myKey", new Gson().toJson(pojo)).apply();
    super.onStop();
}
F43nd1r
  • 7,690
  • 3
  • 24
  • 62
  • Should I check if my SharedPreferences is null in order to run the code as the first time? – FET Jul 14 '16 at 16:44
  • Thus, how do I pass an ArrayList of custom Object (Player in my case) though SharedPreferences? – FET Jul 14 '16 at 17:09
  • Yes, you have to check if the values exist before using them. the SharedPreferences itself will never be null. To save custom objects you have to serialize them in one way ore the other. If they are POJOs, I'd recommend GSON. – F43nd1r Jul 14 '16 at 17:21
  • Ehm.. may I ask you a little snippet to see how to implement all this? I mean, how I should check at the beginning the null before reading the new values, and how to save the custom objects. That would be the bounty, thanks! – FET Jul 14 '16 at 17:34
  • Yes, I am using Google's Gson v2.7, which should be one of the recent versions. – F43nd1r Jul 14 '16 at 18:43
  • Hey, what if I have a List of custom Objects (List – FET Jul 15 '16 at 10:27
  • The best way to save lists with Gson is to convert them to arrays before saving and convert them back afterwards. – F43nd1r Jul 15 '16 at 17:50
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/117453/discussion-between-fet-and-f43nd1r). – FET Jul 15 '16 at 20:32
  • Hey F43nd1r, got a small bug in your code on GitHub, saw our updated chat above? – FET Aug 05 '16 at 09:08