I was trying to store a MutableMap in shared preferences so I could retrieve it later, but my app is crashing when retrieving and casting the mutableMap.
First I initialized the map and filled it with values
val mapYearMonths: MutableMap<Int, Map<Int, Map<Int, Map<Map<String, Int>, Int>>>> =
HashMap<Int, Map<Int, Map<Int, Map<Map<String, Int>, Int>>>>()
Then I stored it, which seems to work correctly:
val stepsRecord: String = Gson().toJson(mapYearMonths)
val editor = sharedPreferences.edit();
editor.putString("stepsRecord", stepsRecord);
editor.commit();
And finally I retrieved it, which crashes at line 3
val mapYearMonthsString: String = sharedPreferences.getString("stepsRecord", "0")!!
val mapYearMonthsAny: MutableMap<*, *>? =
Gson().fromJson(mapYearMonthsString, MutableMap::class.java)
return mapYearMonthsAny as MutableMap<Int, Map<Int, Map<Int, Map<Map<String, Int>, Int>>>>
This is the error I get:
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.walker/com.example.walker.MainActivity}: com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was NUMBER at line 1 column 2 path $
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3308)
Any clue on what I could be doing wrong? I tried looking up but couldn't find anything on this. What am I doing something wrong? Should I use another method to store the data? Thanks everyone for the help in advance!
"0" is not a JSON object and cannot be parsed as a MutableMap. Use a default value that can be parsed as a MutableMap, such as "{}" for an empty map.
Should I use another method to store the data?
Possibly, but we do not know how you are using this data, how big this Map is, why you chose SharedPreferences in the first place, etc.
Related
I will try to do my best to explain the problem and thanks
im using sharedpreferences to save my data as string and to add new data i add it in an array and
the sharedprefrences file (tab is an arrayString)
tab.add(textToShow)
customadapter.notifyDataSetChanged()
editor.putString(textToShow , textToShow) // Ex : key = "hello" , value = "hello"
editor.apply()
but when i get my data when i open the app, the data in sharedpreferences file are note save with the order of the insert ( i insert at the end but it will be in order).
So i need to know how to save my data always at the end without order
this is how i get my data
mysharedpreferences = getSharedPreferences(fileName , 0 ) // 0 = MODE_PRIVATE
val editor : SharedPreferences.Editor = mysharedpreferences.edit()
/** fin 1**/
/** 2) get all data from the file and put it in mymap **/
val mymap : Map<String , *> = mysharedpreferences.all
/** fin 2**/
/** 3) initialise the table and show data **/
for ( (key , value) in mymap ) {
tab.add(value.toString())
customadapter.notifyDataSetChanged()
thelistv.adapter = customadapter
}
That's not how Maps work. A Map class is unordered, by design. There is no promise that the data comes out in the order it goes in. If that's what you need, anything that stores data in a Map in memory will not work. That includes SharedPreferences. Instead, you need to serialize it to a file yourself.
Good day. Is there some way that I could implement this one?
val db = Firebase.firestore
val userID = Firebase.auth.currentUser!!.uid
val infoRef = db.collection("user").document(userID).collection("profile").document("info")
infoRef.get()
.addOnSuccessListener {document ->
if(document != null){
//get the data as cast to hashmap
val data = document.data as HashMap<*, *>
//get the username field and set text for greet user as the same value inside firestore
val username = data["username"] as String
tv_greet_user.text = "Hello, $username"
}
}
//extract the code above as a new method called "getUsername()"
val username : String = getUsername()
tv_greet_user.text = "Hello, $username"
Yes, you can create a function that looks like this:
fun getUsername(data: HashMap<String, Any>) = data["username"] as String
And inside your callback simply call:
val username = getUsername(data)
Is there a way to extract the whole block into a separate method? So that in the onCreate method, I could simply change the TextView into something like: val username = getUsername() tv_greet_user.text = "Hello, $username"
Edit:
As also Frank van Puffelen mentioned in his you cannot return the result of an asynchronous operation as a result of a method. Since you are using Kotlin programming, please note that there is actually o solution. I wrote an article called:
How to read data from Cloud Firestore using get()?
In which I explained four ways in which you can get data from Firestore. So if you are willing to use Kotlin Coroutines, then things will be much simpler.
Data is loaded from Firestore (and most modern web/cloud APIs) asynchronously. Since it may take some time before the data is available, your main code continues running while the data is being loaded. Then when the data is available, your success listener callback is called with that data.
This unfortunately means that it is impossible to return the value from the database in a function, because by the time the return statement runs, the data hasn't been loaded yet.
And that's also precisely why infoRef.get() in your code doesn't simply return the value from the database, but requires that you pass in a callback function that it invokes when the database is available. Sure, your code would be a lot simpler if get() would immediately return the value from the database, but it can't do that because the data needs to be loaded from the network.
I recommend reading:
The Kotlin docs on asynchronous programming techniques
Why does my function that calls an API return an empty or null value?
How to return a DocumentSnapShot as a result of a method?
How to check a certain data already exists in firestore or not
I am trying to show data from the server using data class of kotlin. It's almost working fine but some case whenever I fetch a response I don't know why it is still giving null values unless I add a default value ("") for msg.
This is my data class
data class ViewcardModel(
val msg: String = "", // here is default values
val cartcnt: String = "",
val order_total: Int = 0,
val status: Boolean = false
)
This is my response from server
{
status = false // server response
}
You're probably using something like GSON to instantiate the instances of your model. These tools use reflection to create instances, and therefore the default parameter value of your primary constructor will never take effect (since it's never called).
What you need is the same as what this question is about:
Setting Default value to a variable when deserializing using gson
A custom deserializer is probably what you'll end up with.
You should pass the value into data class like this
val s= ViewcardModel(status=false)
println(s.toString())
val s1= ViewcardModel(msg="hello",status=false)
println(s1.toString())
output
ViewcardModel(msg=, cartcnt=, order_total=0, status=false)
ViewcardModel(msg=hello, cartcnt=, order_total=0, status=false)
I have a probleme here.
I don't know how to read all values of a SharedPreferences for one particular key.
Actually I'm trying to write an Arraylist in preferences , then read it.
Let me explain with some code, Here is my methods for write in preferences :
fun writeArrayOnPreferences(key: String?, array: ArrayList<String>, c:Context) {
val preferences = c.getSharedPreferences(
c.getString(key), Context.MODE_PRIVATE)
with(preferences.edit()) {
for (value in array) {
putString(key, value)
}
commit()
}
}
My writing code works , it's persistent , but I don't really understand how to READ this Arraylist from preferences.
I tried a lot of things to read this but it show me only the last element wrote in preferences
I really want you to understand that I want multiple values for a specific key
This is a quick example based on Nabin Bhandari's answer
fun writeArrayOnPreferences(key: String, array: ArrayList<String>, c:Context) {
val jsonString = Gson().toJson(array)
pref.edit().putString(key, jsonString).commit()
}
fun readArrayFromPreferences(key: String, c: Context): ArrayList<String> {
val jsonString = pref.getString(key)
val array = Gson().fromJson(jsonString, ArrayList<String>()::class.java)
return array
}
ok here how it is! If you want multiple data against one key in share pref editor then SET is your solution as After API 11 the SharedPreferences Editor accept Sets. You could convert your List into a HashSet or something similar and store it like that When your read it back, convert it into an ArrayList, sort it if needed and you're good to go.
//Set the values
val yourSet = HashSet<String>()
set.addAll(listOfExistingScores)
yourPrefEditor.putStringSet("key", yourSet)
yourPrefEditor.commit()
//Retrieve the values
val yourSet = yourPref.getStringSet("key", null)
SOLUTION NUMBER 2
would be like serializing the ArrayList and passing it! but there can be a catch if any value in your array does posses any rule that can't be Parced it will crash!
For More check this Thread this is in java but it will help tell you more!
You cannot simply loop values in an ArrayList put them in the preferences using same key for all values and expect to retrieve the ArrayList back.
You can convert the 'ArrayList' in JSON format and store it in SharedPreferences. Then to parse the JSON string to get the ArrayList.
You can make this process easier with the help of a library called Gson.
Try this:
with(preferences.edit()) {
var s = ""
for (value in array) {
s = s + value + ","
}
putString(key, value)
commit()
}
your array will be saved like comma separated values which when read back in a string, by using the split function will become an array.
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, "" );