30

I have a couple objects, Location, in my app stored in an ArrayList and use parcelable to move these between activities. The code for the object looks like this:

public class Location implements Parcelable{

private double latitude, longitude;
private int sensors = 1;
private boolean day;
private int cloudiness;

/*
Måste ha samma ordning som writeToParcel för att kunna återskapa objektet.
 */
public Location(Parcel in){
    this.latitude = in.readDouble();
    this.longitude = in.readDouble();
    this.sensors = in.readInt();
}

public Location(double latitude, double longitude){
    super();
    this.latitude = latitude;
    this.longitude = longitude;
}

public void addSensors(){
    sensors++;
}


public void addSensors(int i){
    sensors = sensors + i;
}

+ Some getters and setters.

Now I am in need of storing these objects more permanently. I read somewhere that I can serialize the objects and save as sharedPreferences. Do I have to implement serializeable aswell or can I do something similar with parcelable?

xsiand
  • 465
  • 1
  • 4
  • 10

6 Answers6

77

Since parcelable doesn't help to place your data in persistent storage (see StenSoft's answer), you can use gson to persist your Location instead:

Saving a Location:

val json = Gson().toJson(location)
sharedPreferences.edit().putString("location", json).apply()

Retrieving a Location:

val json = sharedPreferences.getString("location", null)
return Gson().fromJson(json, Location::class.java)

In case you're still using Java, replace val with String, Gson() with new Gson(), ::class.java with .class and end each line with a semicolumn.

Cristan
  • 12,083
  • 7
  • 65
  • 69
  • Somehow Gson().toJson(location) is not working as expected, it is just giving back empty braces like {} while location is there. `Saving best location Location[gps 37.343339,-122.097152 hAcc=1.7821856 et=+1m54s707ms alt=46.24608612060547 vel=0.6257401 mock] string>> {}` – Arpit Dec 02 '22 at 14:00
39

From documentation of Parcel:

Parcel is not a general-purpose serialization mechanism. This class (and the corresponding Parcelable API for placing arbitrary objects into a Parcel) is designed as a high-performance IPC transport. As such, it is not appropriate to place any Parcel data in to persistent storage: changes in the underlying implementation of any of the data in the Parcel can render older data unreadable.

StenSoft
  • 9,369
  • 25
  • 30
  • Ah! In that case I suppose I'll just have to serialize it instead. – xsiand Feb 10 '15 at 18:34
  • @xsiand Relevant: http://stackoverflow.com/questions/5418160/store-and-retrieve-a-class-object-in-shared-preference – Micro Mar 06 '16 at 23:55
2

If you are using Kotlin, I would take the approach of Cristan, but with some extension functions, see:

import android.content.SharedPreferences
import android.os.Parcelable
import com.google.gson.Gson
import com.google.gson.JsonSyntaxException

fun SharedPreferences.Editor.putParcelable(key: String, parcelable: Parcelable) {
    val json = Gson().toJson(parcelable)
    putString(key, json)
}

inline fun <reified T : Parcelable?> SharedPreferences.getParcelable(key: String, default: T): T {
    val json = getString(key, null)
    return try {
        if (json != null)
            Gson().fromJson(json, T::class.java)
        else default
    } catch (_: JsonSyntaxException) {
        default
    }
}

And then you could use it as follows, for storing:

sharedPreferences.edit {
    putParcelable("location", location)
}

And for reading:

val location = sharedPreferences.getParcelable<Location?>("location", null)

This is a pretty clean way of using the Cristan proposal. Hope it works for you :)

Arnyminer Z
  • 5,784
  • 5
  • 18
  • 32
2

Since Parcel should not be used to be stored in the Shared Preferences, an alternative could be to use the new Kotlin Serialization.

Here are two extension functions that add serializable to the Shared Preferences:

inline fun <reified S> SharedPreferences.getSerializable(key: String): S? {
    return getString(key, null)?.let {
        Json.decodeFromString(it) as? S
    }
}

inline fun <reified S> SharedPreferences.putSerializable(key: String, value: S) {
    val jsonString = Json.encodeToString(value)
    edit().putString(key, jsonString).apply()
}

which then can be used like this:

@Serializable
data class Location {
  val double latitude,
  val double longitude
}

val sharedPrefs = context.getSharedPreferences("mySharePrefs", Context.MODE_PRIVATE)
val location = Location(1.1, 2.2)
sharedPrefs.putSerializable("location", location)
val locationFromSharedPrefs = sharedPrefs.getSerializable<Location>("location")
Stephan
  • 15,704
  • 7
  • 48
  • 63
0

You can create class both Gson'able and Parcelable like

@Parcelize
data class ApiRate(
    @SerializedName("tp") val tp: Int,
    @SerializedName("name") val name: String,
    @SerializedName("from") val from: Int,
    @SerializedName("currMnemFrom") val currMnemFrom: String,
    @SerializedName("to") val to: Int,
    @SerializedName("currMnemTo") val currMnemTo: String,
    @SerializedName("basic") val basic: String,
    @SerializedName("buy") val buy: String,
    @SerializedName("sale") val sale: String,
    @SerializedName("deltaBuy") val deltaBuy: String,
    @SerializedName("deltaSell") val deltaSell: String
) : Parcelable

Possible.

-5

The preferred way would be to implement an IntentService probably

LowFieldTheory
  • 1,722
  • 1
  • 26
  • 39