Using Shared Preferences with arrays - android

EDIT:
OK It turns out this code was working (more or less) I'd left in a line that reset the booleans I was trying to change. Thanks everyone for the help though.
Having trouble using SharedPreferences to read in saved array data when my app starts.
My _dPad Boolean and my _FreePlay Integer loads, saves and passes to and from my _renderer without any problems.
The trouble starts when I try and use some arrays
easteregg[] only has 2 entries right now so obviously I could just just turn them into separate variables but I wish to add more arras of longer length so this makes a convenient test example.
I've noted on the code what appears to happen (the easteregg[] settings just doesn't appear to have changed)
to read data:
// Read saved preferences
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
_renderer._dPad = prefs.getBoolean("_dPad", false); // * works ok *
_renderer._FreePlay = prefs.getInt("_FreePlay", 1); // * works ok *
_renderer.easteregg[0] = prefs.getBoolean("easteregg[0]", false ); // * not working
_renderer.easteregg[1] = true; // * even this is not working
setRenderer(_renderer);
to write data:
public void onDetachedFromWindow() {
super.onDetachedFromWindow();
// As good a time as any to save current config
save = false ; // don't commit if nothing changed.
SharedPreferences prefs =
PreferenceManager.getDefaultSharedPreferences(getContext());
SharedPreferences.Editor editor = prefs.edit();
if (_renderer._dPad != prefs.getBoolean("_dPad",false)){ save = true ;
editor.putBoolean("_dPad", _renderer._dPad);}
if (_renderer._FreePlay != prefs.getInt("_FreePlay",1)){ save = true ;
editor.putInt("_FreePlay", _renderer._FreePlay);}
editor.putBoolean("easteregg[0]", _renderer.easteregg[0]);
editor.putBoolean("easteregg[1]", _renderer.easteregg[1]);
if (save == true){editor.commit();}
}
And in the .renderer class
// START SAVE DATA
public boolean _dPad ; // false no Virtual Pad *Works Fine*
public int _FreePlay ; // 1 = no free play *Works Fine*
public boolean[] easteregg = new boolean[2]; *Values don't load or save*
//public boolean easteregg[]; // tried this first *CAUSES CRASH*
// END SAVE DATA
Do I have to convert the arrays to strings? I don't get how to change them.

I put your code into a quick activity, creating just the shell of the renderer class as you have above and found that your save boolean is false, so it never commits the preferences.
I forced the save to true, and played around with it and everything worked fine from there.
I'd recommend adding checks to the easter eggs the same as you have for any other preference; test to see if the current value is the same as the saved value, and if not, set the save flag.

I would suggest saving the array as a string in a single variable. It appears you have an array of booleans. So loop through it to make it a series of either ints (0, 1) or the string "true" or "false" then save it to an int or string.
I suspect the probelm might be that your setting name contains square brackets. I think that in key value names, the key name must be a valid variable name. And square brackets are not allowed in variable names.
However i would also expect this to throw an error. Does the code work if you name you settings "easteregg_01" and "easteregg_02"?

The best solutions would be to convert your array into JSON string and store it as preference value. If you have small amount of data, you can as well stick with org.json classes provided by android. If you have more data, GSON pull parser would be better, as it utlizes pull parser. And if you are really lazy, you grab my small databinding library and do:
String jsonState = preferences.getString(GAME_STATE, null);
StateStorage storage = JSONUnmarshaller.unmarshall(new JsonReader(new
StringReader(jsonState)), StateStorage.class);
and it will instantiate java class for you and fill in the data. And to save:
SharedPreferences.Editor editor = getPreferences(MODE_PRIVATE).edit();
StringWriter writer = new StringWriter();
JsonWriter jsonWriter = new JsonWriter(writer);
JSONMarshaller.marshall(jsonWriter, ss);
editor.putString(GAME_STATE, writer.toString());
editor.commit();
Databinding library is available on github, or from maven central:
https://github.com/ko5tik/jsonserializer
PS: at the moment I work on injection of preference values ( at the moment primitives only):
https://github.com/ko5tik/andject

Related

SharedPreference StringSet to Array Order Issue

Please be patient while I explain my issue:
1) I am storing my preferences via a StringSet as follows:
SharedPreferences.Editor editor = getSharedPreferences(MY_PREFS_NAME, MODE_PRIVATE).edit();
// Create a new Arraylist with the details of our details
ArrayList <String> newCityFareDetails = new ArrayList<String>();
// Store various values
newCityFareDetails.add(0, String.valueOf(cloneFare.value1()));
newCityFareDetails.add(1, String.valueOf(cloneFare.value2()));
newCityFareDetails.add(2, String.valueOf(cloneFare.value3()));
newCityFareDetails.add(3, String.valueOf(cloneFare.value4()));
newCityFareDetails.add(4, cloneFare.value5());
// Only value 5 is a string, rest are all floats
// Convert to a hashstring, give it the name of our value
Set<String> set = new HashSet<String>();
set.addAll(newCityFareDetails);
editor.putStringSet(extras.getString("startCity"), set);
// And write it to storage
editor.commit();
Now, I'm trying to read it as follows:
SharedPreferences prefs = getSharedPreferences(MY_PREFS_NAME, Context.MODE_PRIVATE);
Set<String> tryCityFromPrefs = prefs.getStringSet(currentCity, null);
if (tryCityFromPrefs!=null){
// Crude code, but we convert the preferences into a String array
String[] values = tryCityFromPrefs.toArray(new String[tryCityFromPrefs.size()]);
myFare = new Fare(Float.parseFloat(values[0]), Float.parseFloat(values[1]),
Float.parseFloat(values[2]), Float.parseFloat(values[3]), values[4]);
}
Now, problem is that the myFare is not getting initialized properly because the values in the array are scrambled. i.e. the String value that was at the last position when we save is now in the 2nd position. Is this something to do with Sets to String conversion? Or am I missing something obvious?
A Set does not guarantee order. While there are specific Set implementations (e.g., LinkedHashSet) that are ordered, that's not what SharedPreferences uses.
Your options are:
Change your app to not care about the order.
Save the data in SharedPreferences some other way. In this app, for example, I use JsonReader/JsonWriter to save an ArrayList into a single String value.
Save the data in some other fashion (e.g., JSON file, SQLite database with a sequence number to maintain order).

How to store an arraylist which has content class (class in project)?

I want to store an ArrayList<class> in shared preference. But the error showed up in editor3.putString("Array", nama);. I guess the error caused by putString. What sould i do?
Should I used another method to storing arraylist ?
ArrayList<Class> nama = new ArrayList<Class>(9);
nama.add(dragsandal.class);nama.add(Terimakasih.class);
nama.add(Ludah.class);
nama.add(Permisi.class);
nama.add(Tolong.class);
nama.add(Maaf.class);
SharedPreferences pref3 = getApplicationContext().getSharedPreferences("Array", MODE_PRIVATE);
SharedPreferences.Editor editor3 = pref3.edit();
editor3.putString("Array", nama);
editor3.apply();
You should use putStringSet(Set<String>) to store sets (Lists with unique elements). SharedPreferences do not provide a method to store lists directly.
You can easily convert your list to a set using e.g. new HashSet<String>(yourList);
If you need to store a list, you can serialize your list to a String, e.g. by using Gson and storing the json value. Then putString(json) would be correct.
First I don't think there is a way to store lists in Shared preferences. Second it is not a good idea. In your case,I would consider using Sqlite database. It would make things easier.
You can't store a Class type object in SharedPreferences. Also you can't store Lists. If you really need to, you can store the full name of the class object as a String. Then when you read the value back you, you can use Class.forName() to convert that string back to a class. It seems weird, but you can do it.
You could try this to save and restore a set of class names:
Set<String> set = new HashSet<String>();
set.put(Terimakasih.class.getName());
set.put(Ludah.class.getName());
set.put(Permisi.class.getName());
set.put(Tolong.class.getName());
set.put(Maaf.class.getName());
SharedPreferences pref3 = getApplicationContext().getSharedPreferences("set", MODE_PRIVATE);
SharedPreferences.Editor editor3 = pref3.edit();
editor3.putStringSet("set", set);
editor3.apply();
Set<String> strings = pref3.getStringSet("set", Collections.emptySet());
Set<Class> classes = new HashSet<Class>();
for (String s : strings) {
classes.put(Class.forName(s));
}

Need to get all sharedPreferences but in correct order of insertion

I need some help in getting all sharedPreferences (keys & values) from my custom preference, but in order that they were originally inserted in the preference file. I currently have the below code but the problem is because getAll() returns a map the order changes.
public List<String> getPrefValues(String pref, Context context) {
Map<String, ?> allEntries = context.getSharedPreferences(pref,
Context.MODE_PRIVATE).getAll();
List<String> command = new ArrayList<String>();
for (Map.Entry<String, ?> entry : allEntries.entrySet()) {
command.add(new StringBuilder(entry.getKey())
.append(":")
.append(entry.getValue()).toString());
}
if (command.isEmpty()) {
return null;
} else {
return command;
}
}
You can store your desired attributes in a LinkedHashSet, because there,
The iteration order is the order in which entries were inserted
Sets are stored in preferences with:
Set<String> mySet = new LinkedHashSet();
insertAttributes(mySet);
SharedPreferences myprefs = getPrefs();
myprefs.edit().putStringSet("myKey", mySet).commit();
This is also applicable to a map structure: simply create one set, that contains all keys, and one, that contains the values.
There is NO facility in SharedPreferences for tracking insertion time. It would be better if you can figure another way (external to SP) to track this value.
Bottom line, there is no way within the current SP structure to understand 'insertion time'.
You can use the prefix as numbers for the keys when you put in the order you want to get them out.
For example: 00data, 01foo, 02cree.
Then put the Set<String> returned from getStringSet in an Array<Set> and sort it -
Set<String> set = prefs.getStringSet(key, new HashSet<String>());
Array<String> a = set.toArray();
java.util.Arrays.sort(a);

How can I save user-created objects to storage in android?

In my app, I have flashcard objects that the user creates themselves. Users can create as many flashcards as they want, but when they exit the app and return they need to be able to see the flashcards that they previously created and be able to delete them. I have it set up so that they can create/delete, but if they exit the app they will all delete automatically. What is the best way to save the information for a flashcard? It has at least 3 strings currently, the title, the front and the back.
I looked at a few, but am not sure how I would include all three strings in the saving options that are on the android developer site.
For example shared preferences, looks like you can only save certain settings, but it allows the user to change those settings.
The internal/external storage, although very different throw up the same problem, how to have unlimited number of objects and especially how to save all three strings separately.
This is the internal storage is shown below.
String FILENAME = "hello_file";
String string = "hello world!";
FileOutputStream fos = openFileOutput(FILENAME, Context.MODE_PRIVATE);
fos.write(string.getBytes());
fos.close();
I don't see how you can save multiple number of objects or 3 different strings.
Does anyone see a solution to my problem?
SharedPreferences seem like the simplest way for you to achieve it, and I think you've misunderstood their usage, or confused the name with a 'Preferences' screen, as you can use the SharedPreferences methods to save anything (well, any basic datatype) persistently.
For example, I use it to save my app's JSON data (which might be a decent way for you to go in terms of saving you users' flashcards in a JSONArray).
/**
* Retrieves data from sharedpreferences
* #param c the application context
* #param pref the preference to be retrieved
* #return the stored JSON-formatted String containing the data
*/
public static String getStoredJSONData(Context c, String pref) {
if (c != null) {
SharedPreferences sPrefs = c.getSharedPreferences("AppPreferences", Context.MODE_PRIVATE);
return sPrefs.getString(pref, null);
}
return null;
}
/**
* Stores the most recent data into sharedpreferences
* #param c the application context
* #param pref the preference to be stored
* #param policyData the data to be stored
*/
public static void setStoredJSONData(Context c, String pref, String policyData) {
if (c != null) {
SharedPreferences sPrefs = c.getSharedPreferences("AppPreferences", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sPrefs.edit();
editor.putString(pref, policyData);
editor.commit();
}
}
Where the string 'pref' is a tag used to refer to that specific piece of data, so for example: "taylor.matt.data1" would refer to a piece of data and could be used to retrieve or store it from SharedPreferences

How to save variables with SharedPreferences

I have my own Objects which I need to store for later use. The User saves this object, it is turned into a JSON String, then when the User is connected to a network, the JSON String is turned back into the object operations are performed on it.
My problem is that, at run time, how do I know how to store the object?
i.e
Gson gson= new Gson();
String pointOfInterest = gson.toJson(point);
SharedPreferences.Editor sharedprefEditor = application_shared_preferences.edit();
sharedprefEditor.putString(?KEY?,pointOfInterest);
What can I use for the value of KEY? If I use an index, it will get reset every time I open or close the app, and this will replace my Objects.
Edit
Sorry I didn't make this clear enough, the method that the above code is in can be run an arbitrary number of times and there could be several pointsOfInterest to store.
First of all, if you use an index, the Preference will stay forever:
For instance:
sharedprefEditor.putString("JSON569",pointOfInterest);
You can also save the index in an other preference; for instance separated by a column:
sharedprefEditor.putString("indexes","569;789;852");
You can, easily check if an instance exists:
myPreference.getString("JSON789","").contentEquals("");
Or get all your instances:
for (int anIndex:indexes)
    Log.i("TAG","Current value: "+myPreference.getString("JSON"+anIndex,""));
Please xplain a little bit more your question, I see no difficulties there/
You can name the key whatever you want, just make it consistent. One way to do it is make a constant in your class for it:
public class MyClass {
private static final String OBJECT_KEY = "myObjectKey";
...
Then when you save your object:
Gson gson= new Gson();
String pointOfInterest = gson.toJson(point);
SharedPreferences.Editor sharedprefEditor = application_shared_preferences.edit();
sharedprefEditor.putString(OBJECT_KEY,pointOfInterest);
When you load it, just use OBJECT_KEY to get a string out of the shared preferences:
String objectString = sharedPrefs.getString( OBJECT_KEY, "" );

Categories

Resources