How to save data to sharedpreference in a flexible way - android

I save chosen items into shared preference, but as the user during runtime get to remove any of these items (from any position), and as I use the saved size (in the code below) to loop through the items, and here's the problem
Say I have 5 items ( item_1 - item_2 - item_3 - item_4 - item_5 )
if the user removed item_2 I update chosen_items_size to be 4 and I remove item_2 from the shared preference.
but when I load the items later I use the (size = which is 4 now), which as in the code below will miss item_5, how to fix this, any suggestions or better approach to achieve what I need?
mSharedPreference = getSharedPreferences("chosen_items", MODE_PRIVATE);
int size = mSharedPreference.getInt("chosen_items_size", 0);
for(int i = 1; i <= size; i++) {
mSharedPreference.getString("item_" + i, null);
}
Knowing that I want to enable drag and drop items, which using the above approach will make it pretty hard (if possible in the first place) to accomplish, any better approach to save & retrieve data / items?
Thank you

If you have a small amount of strings you can store them in one delimited string in SharedPreferences like so. You could also associate some metadata with each item and delimit that too.

You could utilize a built in SQL lite database.
Is this data that must persist between sessions? if not store it locally with a class and static variables/arrays.

Related

Shared Preferences - Should I store key values as strings? Possible bad practice

I'm using shared preferences and am a bit unsure on what the best practice is on what data type to store key value pairs.
Example: I have a spinner with 3 drop down options: apple, banana, orange
In my spinner, I select the option orange and save it to shared preferences with the key KEY_SPINNER_FRUIT_OPTION what data type should the value be? Currently I'm saving it as Strings
orange
Would it be better to use Integer instead?
String lastSelectedSpinnerOption = getSharedPref().getLastSavedValue("KEY_SPINNER_FRUIT_OPTION");
if(lastSelectedSpinnerOption.equals("orange")){
mSpinner.setSelection(2);
}
or
Integer lastSelectedSpinnerOption = getSharedPref().getLastSavedValue("KEY_SPINNER_FRUIT_OPTION");
if(lastSelectedSpinnerOption == 2){
mSpinner.setSelection(2);
}
If you will store Integer your code won't be maintanable. If you change positions of elements(or add new element in saved position) in Spinner, your code won't work as expected.
What you can do is storing string value of chosen item into SharedPrefs and then:
int position = adapter.getPosition(myValueFromSharedPrefs);
position = position > 0 ? position : 0; // if this element was removed, choose first item of the list
mSpinner.setSelection(position);
Well there's no any best practice as on what type you should write in SharedPreferences (as long as it is an accepted type for the API).
It actually totally depends on what you will do with this value.
If your goal is to save UI state and you're absolutely sure that the retained position will hold the same String value, you could save the Integer.
But I'd rather save the String value as it is the user's intention to select an "orange" and when it is time to restore the UI state, I'd look for the position matching the "orange" value.

Trying to store mutableListOf<String> to SharedPreferences but cannot set default value

Ultimately I am trying to store an Int Array in Shared Preferences but I know Kotlin doesn't support that. So I am converting my Int Array to a String Array using the method here:
How can I store an integer array in SharedPreferences?
My issue is that I am struggling to put in a default value for the getStringSet method:
private fun loadIntScoreArray() {
val prefs = getSharedPreferences(SHARED_PREFS, Context.MODE_PRIVATE)
//TODO: Load the String array
var default = emptyList<String>()
avgScoreArrayString = prefs.getStringSet(AVG_SCORE_ARRAY, default)
}
However default is not an acceptable object in the prefs.getStringSet(AVG_SCORE_ARRAY, default) line. The error is confusing because it seems contradictory:
Required: MutableList
Found: (Mutable)Set!
Required: (Mutable)Set!
Found: MutableList
There is few things you need to know. Since API 11 you can only store plain objects or sets to shared preferences. You can convert your list to set, but it can be lossy conversion in your list contain duplicates.
If you want to use sets you should call it like this:
//to get
prefs.getStringSet(AVG_SCORE_ARRAY, emptySet<String>()))
//to set
prefs.edit().putStringSet(key, AVG_SCORE_ARRAY)
The other way is to join array to a single string using join operation. Here is a doc for you https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/join-to.html
To be honest both of these ways are not the perfect solutions. If it is production application and I recommend using persistance library like Room, Realm etc.
Hope it helps.
Edit.
If you are hundred percent sure you are going to have 5 ints stored (and it is not gonna change in a near or distant future), using database could be overkill. I recommend using joining to single string and storing it as single string or just store 5 independent int values. There is no point in complicating simple things.

Android Saving Information To Device

I'm making an Android application and want to create a "Favorites" list for some objects in the app. I wanna make the list accessible and editable in all my activities and I can't really figure out the best way to do this.
Shared preferences? Writing a small txt file to the device? What's the fastest way to do this?
Thanks in advance!
dependencies {
compile 'com.google.code.gson:gson:2.3.1'
}
Then when you want to save, convert your array into String:
ArrayList<Type> yourData = new ArrayList<Type>();
String dataStr = new Gson().toJson(yourData);
//put this dataStr in your shared preferences as String
To retrieve and convert back to an object is also simple:
String str = "";
//you need to retrieve this string from shared preferences.
Type type = new TypeToken<ArrayList<Type>>() { }.getType();
ArrayList<Type> restoreData = new Gson().fromJson(str, type);
If you want to create a Favorites list, use a SQLite Database.
There's really only four ways to store data.
Shared Preferences
Databases
Local files
Remote Server - Slowest since it depends on network connection, so let's skip this.
Between the remaining 3, SharedPreferences is a great option when used to store a single value. However, it's not a good option for storing a Favorites list, mainly because you can only store a single String.
You can still store it by combining all items in your list into one string, then splitting it each time. However, as your Favorites list gets larger, this single long String will too. Splitting and combining all the time isn't efficient.
SharedPreferences is still a decent option if you only have to store the Favorite's list, but since you want to edit it too, it becomes a less attractive solution.
Local Files and Databases are the better options, however local files require you to read in the file each time you want to use it. This process of reading and writing to a file isn't as efficient as using a Database, especially if you want to edit. For example, let's say you want to remove an item from the middle of your Favorite's list. This would require you to read in the file, edit it, then write the change into the file again. Not too pleasant when compared with the ease of the final solution.
Databases are the best option for this, mainly because it's designed to manage data. You can create a single Favorite's table and add each item as it's own individual row. Fetching the entire table becomes quick and easy. Fetching a single item becomes quick and easy. Adding a new item or removing a new item is also quick and easy.

Listview items are recreated in wrong order

I got a Listview with some String items in it. Im trying to save listview's state in fragment's onStop() method into SharedPreferences:
int i = 0;
HashSet resultsSet = new HashSet(resultAdapter.getCount());
while (i < resultAdapter.getCount()) {
resultsSet.add(resultAdapter.getItem(i)); i++;
}
editor.putStringSet("RSLTS", resultsSet);
editor.commit();
This is how I recreate Listview's state after relaunching app:
if (resultsSet != null) {
for (String result : resultsSet) { resultAdapter.insert(result, 0); }
}
As you can see, Im inserting every new line allways at first position (0) of Listview. Problem is that Listview is recreated in some weird order, for example:
Listview before saving:
3... | 2... | 1...
Listview after reloading: 2... | 1... | 3...
Cant figure it out. Why is this happening? Does anybody have some clue whats wrong with it?
You are using HashSet and according to the documentation:
This class implements the Set interface, backed by a hash table (actually a HashMap instance). 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. This class permits the null element.
So you will need to implement either TreeSet or LinkedHashSet. An arraylist is also a simple alternative.
HashSet are usually implemented with no defined order, so there is no guarantee the order will remain constant over time or when they are written to strings.
Therefore, your options are:
1) sort your String items if applicable to your case (eg alphabetical)
2) save your data to your shared pref as a concatenated string with a given separator (that you are certain doesn't appear in your data). Then, when you read back it, split the string you get from the shared pref with the given separator, to get the array of original strings.

Android - Storing data without a database

My application records user movement with Geofence boundaries, if the user exits the Geofence, alerts are appropriately escalated. These alert are counted and displayed in a summary at the end of the activity. However I would like to create a stats page where it displays the last week or month of activities as well as the number of alerts so that I can display these in a chart. Is there anyway to do this effectively without using a database?
I had thought of writing data to a log file and reading it but curious as to if there is a better option.
You can use SharedPreferences but it will require a lot of controls, probably more then creating a database. If you insist not to use a database, put an integer to your shared preferences saving the count of your data, also that integer will become your id. Then you can store your data with a loop depending on your data.
Here is to write your data to shared preferences
SharedPreferences mSharedPrefs = getSharedPreferences("MyStoredData",
MODE_PRIVATE);
private SharedPreferences.Editor mPrefsEditor = mSharedPrefs.edit();
int count = mSharedPrefs.getInt("storedDataCount", 0);
for(int i = 0 ; i < yourCurrentDataCount ; i++) {
mPrefsEditor.putInt("data" + count, yourData.get(i));
count++;
}
mPrefsEditor.putInt("storedDataCount", count);
And to get your data,
int count = mSharedPrefs.getInt("storedDataCount", 0);
for(int i = 0 ; i < count ; i++) {
yourData.add(mSharedPrefs.getString("data" + i, "defaultData"));
count++;
}
Edit:
I should have added some explaining. The idea is to save the count of your data to generate an id, and save the tag according to it. This code will work like this, lets say you have 5 strings. Since you don't have a MyStoredData xml, it will get created. Then since you don't have the "storedDataCount" tag, you will get 0 as a count. Your loop will iterate 5 times and in each iteration, you will add a tag to your xml like "<.data0>your first data<./data0><.data1>your second data <./data1>... After your loop is done, you will modify your storedDataCount and it will become <.storedDataCount>5<./ storedDataCount>. And the next time you use your app, your count will start from 5 so your tag will start from <.data5>. For reading, you will iterate through tags by checking "data0", "data1" and so on.
You can use java serialization if you dont want to use database.
You can also use XML/JSON for storing data.
I support already mentioned favoritism towards using a DB for this task. Nevertheless, if I were to do it via FS, I would use a transactional async library like Square's tape is.
In your case I would keep the data during a session in JSON object (structure) and persist it (in onPause()) and restore it (in onRestore()) with tape's GSON Object Converter.
Should be easy out of the box, I believe.
Tape website: http://square.github.io/tape/
Alternatively to manually persisting a file or using a 3rd party library like tape, you could always (de)serialize your JSON to SharedPreferences.

Categories

Resources