SharedPreference Set<String> is resetting every time activity is recreated - java

My code here will be simplified for readability.
Methods for getting the Set and adding a password to it:
public void addPass(String pass)
{
// Get the current list.
SharedPreferences sp = getSharedPreferences("passes", 0);
SharedPreferences.Editor editor = getSharedPreferences("passes", 0).edit();
Set<String> passes = sp.getStringSet("myStrings", new HashSet<String>());
// Add the new value.
passes.add(pass);
// Save the list.
editor.putStringSet("myStrings", passes);
editor.commit();
}
public Set<String> getPasses()
{
SharedPreferences sp = getSharedPreferences("passes", 0);
return sp.getStringSet("myStrings", new HashSet<String>());
}
Reading from the Set
Set<String> x = getPasses();
String[] passes = x.toArray(new String[0]); // convert the set to array
toast(Arrays.toString(passes)); // test what the set looks like
I converted the set to an array because it was easier to test conditions with an array for me.
Adding to the Set
EditText password1 = new EditText(this);
String p1 = password1.getText().toString();
addPass(p1.trim()); // add it to the set
toast("Account successfully created.");
Problem
When I first ran this code, I added three String values: "a", "b", "c" (in that order).
All the value were added to the Set correctly and it was confirmed with this line of code from above:
toast(Arrays.toString(passes)); // test what the set looks like
It outputted [b, c, a] after the "c" was added to the set.
The problem is that when I closed the app and reopened it, the Set only contained "a".
I could add the "b" and "c" values again, but the cycle would just continue where the Set only kept the first value added to it after the activity was recreated.
I've troubleshooted for a while and cannot fix it. I can provide more detail on how and when I'm using this code if necessary. I'm hoping I don't need to and someone can point out a problem in the code as shown.

Was searching for a solution for the same issue, a solution to this would be
// Get the current list.
SharedPreferences sp = getSharedPreferences("passes", 0);
SharedPreferences.Editor editor = getSharedPreferences("passes", 0).edit();
Set<String> passes = sp.getStringSet("myStrings", new HashSet<String>());
//Make a copy, update it and save it
Set<String> newPasses = new HashSet<String>();
newPasses.add(pass);
newPasses.addAll(passes);
editor.putStringSet("myStrings", newPasses); editor.commit();

It might be that your issue stems from using getStringSet(), have a look at the docs:
public abstract Set<String> getStringSet (String key, Set<String> defValues)
Retrieve a set of String values from the preferences.
Note that you must not modify the set instance returned by this call. The consistency of the stored data is not guaranteed if you do, nor is your ability to modify the instance at all.

Based on this doc , and this SO Post about the exact same problem as yours, it seems you should create a new copy of the returned instance of the HashSet, and you seem to have done that, at least from what code you've pasted here. Maybe there's something else that's being missed.
However, check out the answer on this post . What the guy did was:
Add the first value to SharedPreferences as a String Set.
To add subsequent values, first read the existing pref into a new String Set.
Remove the old String Set key.
Modify the new string set to add the new values you want to store.
Add the new String Set to preferences.
While that is a long and silly way to do things, it seems like the last option. So basically, store all the values you want to store, in one go (not in instalments). If you want to add new values, remove the old key, create a new key with the old + new values, then store the new key.
Hope that helps.

Related

Cannot put a Set<String> in sharedPreferences.putStringSet()?

Good morning! I am trying to make a toDo list and I want to do this through the sharedPreferences. The problem is that once I get the contents of the location and then add it to the contents of the list and try to push it back to sharedPreferences, I get a strange error. It does not make sense to me because the taskSet Set is defined by the items in the sharedPreferences, so how could it be the incorrect type?
public void putInSharedPreferences(View view){
Intent i = getIntent();
SharedPreferences sp = getSharedPreferences("storage", Context.MODE_PRIVATE);
Set<String> taskSet = sp.getStringSet("taskSet", new HashSet<String>());
String task = i.getStringExtra("taskText");
taskSet.add(task);
sp.edit().putStringSet(taskSet).apply();
int minutes = i.getIntExtra("chosenMinutes", 0);
}
The error I receive:
putStringSet (java.lang.String, Set) in Editor cannot be applied to (java.util.Set)
 
putSetString() takes two parameters: A key, which is aString, along with a Set<String>. So you'd put the set like this:
sp.edit().putStringSet("taskSet", taskSet).apply();
See the method definition here for more info.

How to store key value pairs in SharedPreferences?

I am developing an app that generates passwords randomly. I am adding a "save" feature so that the user can save their passwords in SharedPreferences. (which is not what you are supposed to do with your passwords)
I have an activity called PasswordActivity and in that activity I display the saved passwords.
This is how I save a password: I prompt the user to enter a name/key thingy for the password so that he/she can identify it later. And then I save the key and the password in the SharedPreferences using the following method in a utility class:
public static int savePassword (Context c, String passwordKey, String passwordValue) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences (c);
Set<String> keys = prefs.getStringSet (PASSWORD_KEYS, new HashSet<String> ());
Set<String> values = prefs.getStringSet (PASSWORD_VALUES, new HashSet<String> ());
SharedPreferences.Editor editor = prefs.edit ();
boolean duplicateKey = !keys.add (passwordKey);
boolean duplicateValue = !values.add (passwordValue);
if (duplicateKey)
return KEY_DUPLICATE;
if (duplicateValue)
return VALUE_DUPLICATE;
editor.putStringSet (PASSWORD_KEYS, keys).
putStringSet (PASSWORD_VALUES, values).apply ();
return SUCCESS;
}
And I wrote another method to get the passwords and their names. The method returns a HashMap<String, String> as you might have guessed.
public static HashMap<String, String> getPasswords (Context c) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences (c);
Set<String> keys = prefs.getStringSet (PASSWORD_KEYS, new HashSet<String> ());
Set<String> values = prefs.getStringSet (PASSWORD_VALUES, new HashSet<String> ());
ArrayList<String> keysList = new ArrayList<> ();
ArrayList<String> valuesList = new ArrayList<> ();
for (String key : keys) {
keysList.add (key);
}
for (String value : values) {
valuesList.add (value);
}
HashMap<String, String> map = new HashMap<> ();
for (int i = 0 ; i < keys.size () ; i++) {
map.put (keysList.get (i), valuesList.get (i));
}
return map;
}
I admit that the code is pretty messy. I first get the things in the two sets and turn them into ArrayLists and add the stuff in the array list to the map and return.
Phew! That was a lot of work!
Now when the user generates a password and saves it, the two sets contain the right things: (These are fake data)
keys:
key1
values:
value1
That's all good. But when I generate another password and save it, the two sets become messy:
keys:
key1
key2
values:
value2
value1
Now when I call the getPasswords method, it will return
{key1, value2}, {key2, value1}
which is incorrect. After looking at the docs, I found out that:
It makes no guarantees as to the iteration order of the set; in particular, it does not guarantee that the order will remain constant over time.
which I guess is the reason that makes the incorrect results.
So I was wondering is there an alternative way to store these passwords and their names?
As you're storing multiple names & passwords, i would suggest a local SQLite database. SharedPreferences are meant for single values, not sets of values.
If you can't be bothered setting up the entire SQLiteOpenHelper and all that raw SQL code, you could take a look at some ORM helpers such as SugarOrm (i currently use this, its EXTREMELY easy to set up and use)
I would recommend a database, such as SQLCipher for Android, so the user can provide a master passphrase for the app, and all the data is stored encrypted on the device.
But, let's pretend that, in order to successfully repel an alien invasion and prevent the wholesale slaughter of countless humans, you have to implement this using SharedPreferences. After that, you will need to fly with a wise-cracking companion in a commandeered alien spacecraft and successfully upload your app into the computer systems of one of the alien mother ships (which, fortunately, happen to be running Android, as Android has remarkable market share across the galaxy).
Either:
Have each key and value be separate entries in the SharedPreferences. After all, SharedPreferences is a key-value store. Use a dedicated preference file for your keys and values (so as not to collide with anything else you might want to store), then you know that all the keys are ones from the user.
Marshal the HashMap into JSON or XML or something and save that as a string, unmarshalling it as needed.

Android: store an ordered list of values between app launches

I want to store the last X values that a user input into a form. I have a list of 20 values and every time the user enters a new one, the oldest value is pushed out of the list and the newest value goes in. I want this list to persist between app launches, but sharedpreferences doesn't support an ordered set, so the results will end up jumbled every time. How can I store the list in an ordered form that I can then retrieve?
Set<String> previousValues = sharedPreferences.getStringSet(getString(R.string.LAST_VALUES), null);
You can do this in several ways:
Create a SQLite database and store them in a table
Have a unique key in the sharedpreferences and use JSON to serialize them. You don't need to use the any external library, Android includes its own JSON classes,
Have multiple keys in the SharedPreference, one for each value, and an additional, numerical, for storing the index of the last one, something like:
int index = ... // number of entries
List<String> values = ... // the values you want to store
SharedPreferences prefs = ...
SharedPreferences.Editor editor = prefs.edit();
editor.putInt("index", index);
for (int i = 0 ; i < index ; i++) {
editor.putString("value." + i, values.get(i)); // assuming your values are in a String collection called 'values'
}
Of course, this snippet should give you the general idea, you would probably only add the last one every time and not all at once. When reading, you read first the index value, and then only index number of strings.
Using SharedPreferences it's easy, after API 11 the SharedPreferences Editor accept Sets as argument, so just create a Set with size of 20 and store it in shared preferences. When retrieved use it like any other java set to store only 20 values.
Set<String> lastValues = new HashSet<String>(20);
Get shared prefs
SharedPreferences prefs = getSharedPreferences(SHARED_PREFS_FILE, Context.MODE_PRIVATE);
Set shared prefs
Editor editor = prefs.edit();
try {
editor.putString(TASKS, ObjectSerializer.serialize(lastValues));
} catch (IOException e) {
e.printStackTrace();
}
editor.commit();
Examples here and here

Android array to Sharedpreferences with Gson

I need to create an array of the last 5 call dates.
I know that I have to save the date on the array but I don't know how to reorder the other records to keep this last call date the 1st record. And always maintain only 5 records
The Goal is to save that last call string and add to the array, after that I reorder to and maintain only the 5 records, after that I use FlexJson to make a string and save on sharedpreferences. Anyone have this full process?
Here what I'm doing but is throwing errors everywhere:
SAVE
SimpleDateFormat df = new SimpleDateFormat("HH:mm:ss dd-MM-yyyy", Locale.getDefault());
Calendar calendar = Calendar.getInstance();
String currentDateTimeString = df.format(calendar.getTime());
List<String> textList = new ArrayList<>();
JSONSerializer ser = new JSONSerializer();
textList.add(currentDateTimeString);
String jsonText = ser.deepSerialize(textList);
editor.putString("lastCall", jsonText);
editor.commit();
READ:
String lastCall = callLogPreferences.getString("lastCall", null);
JSONDeserializer<List<String>> der = new JSONDeserializer<>();
List<String> textList = der.deserialize(lastCall);
I'm using FlexJson: GSON and InstanceCreator issue
It's not converting from string to Array List
First let me give you an advise - if you have 3 question create 3 topics, not one with all the questions. Based on the question title: Android array to sharedpreferences I'll give an answer just to this issue.
So from your question you want to store a List<String> myList inside SharedPreferences. To do so I advise you to use Gson because it's simple. The main idea it's to serialize your List to a String in json and then store that String:
Since you are using primitive types you don't need to specify a TypeToken so you just need to:
public static final MY_LIST = "my_list";
List<String> myList = new ArrayList<String>();
SharedPreferences.Editor editor = prefs.edit();
editor.putString(MY_LIST, new Gson().toJson(myList));
editor.commit();
And it's stored. To retrieve the list just do the opposite:
myList = new Gson().fromJson(prefs.getString(MY_LIST, null), List<String>);
And that's it!
Hope it helped.
ps: SharedPreferences it's used to store values that can be accessed in other Classes/Activities/etc.. or to persist information between app restarts. You don't need this to accomplish what you want: Create a list with 5 ordered strings.
EDIT: this lib may save you some time with the serialization ;)
Try using an ArrayList with a custom Comparator as seen here: Sort ArrayList of custom Objects by property
Then, sort the ArrayList accordingly.

Adding new Key Value Pair Replaces all values in Java HashMap

I have a HaspMap which has "Text" objects as the key and "Integer" Object as Values. The Value is actually the number of Occurrences of the Key in my code. So for the first time it will be 1 and then it keeps incrementing. The Code is shown below.
First I check if a given "Text" Object exists in the Map. If it does not then I add it as a Key to the Map along with a Value of 1. But the problem that I am facing is that when I add the new Key and value to the map through "put" function for some reason all the previously present Key/value pairs in the Map are getting replaced with the new One. The code is given below.
public class WordPatternReducer extends Reducer<IntWritable,Text, Text, IntWritable>{
private IntWritable totalWordCount = new IntWritable();
private Map<Text,Integer> valueCount=new HashMap<Text,Integer>();
private Map<IntWritable,HashMap<Text,Integer>> posMap=new HashMap<IntWritable, HashMap<Text, Integer>>();
#Override
public void reduce(IntWritable key, Iterable<Text> values, Context context)
throws IOException, InterruptedException {
Iterator<Text> it=values.iterator();
Integer maxCountInteger=new Integer(1);
Text maxOccurText = null;
Text newval=new Text();
while (it.hasNext()) {
newval=it.next();
System.out.println("The new val outside is"+newval);
if (valueCount.containsKey(newval)) {
System.out.println("The new val inside if is"+newval);
valueCount.put(newval, (valueCount.get(newval)+1));
} else {
System.out.println(newval);
valueCount.put(newval, 1);
System.out.println(valueCount.toString());
}
maxOccurText=newval;
} }}
So the check for existing keys is working as it always go to the else statement. But the values are getting replaced. In the output console I am getting the following output.
The new val outside isWelcome
Welcome
{Welcome=1}
The new val outside isservice service
{service=1, service=1}
The new val outside isversions versions
{versions=1, versions=1, versions=1}
The new val outside isto
to
{to=1, to=1, to=1, to=1}
The new val outside isproviders,
providers,
{providers,=1, providers,=1, providers,=1, providers,=1, providers,=1}
The new val outside isof of
{of=1, of=1, of=1, of=1, of=1, of=1}
The new val outside isthe the
{the=1, the=1, the=1, the=1, the=1, the=1, the=1}
The new val outside issome some
{some=1, some=1, some=1, some=1, some=1, some=1, some=1, some=1}
and so on..
I do not want this. I just want to add the new Key value pairs retaining old one. Could someone please let me know what I have done wrong? Thank you in advance.
You're storing a reference to newval in the map. The containsKey(newval) checks false because Text checks to see if its contents are the same, but then the put call stores a reference to same newval that you've put in the map repeatedly (which contains only the latest string read in). Try changing the map to a map of String,Int and calling map.put(newval.toString()) to start, which should lead you to a better solution. Else, declare newval within the iterator loop so that you're storing a new Text object in the map each time (Text newval = it.next()).
What class is Text?
Hypothesis 1: Text doesn't implement equals() and hashCode() according to the contract. That'll mess up a map.
Hypothesis 2: Text is mutable and changing during the iteration. Using String keys in the map would fix that.
It looks like your Text value is mutable. What is the implementation of the Iterable values object? Is it a parser that repeatedly returns the same token instance, modifying its type and text with each call to next()? If so, you'll need to copy the text to a new immutable object—which might simply be a String—and use that immutable token as your key.

Categories