Storing ArrayList in onSavedInstanceState - android

I'm running an asynctask in a Fragment class and populating a ListView with the result. However I only run this asynctask the first time this fragment is instantiated.
How do I store the the ArrayList of objects created in the asynctask in onSavedInstance so I can retrieve them later?
I tried using putParcelableArrayList but I still get a NullPointerException when I try to retrieve it.
Thanks for your help
Stops:
public class Stops implements Parcelable{
private String station_name;
private ArrayList<Stops> stopTitles;
public Stops()
{
}
public Stops(Parcel source)
{
station_name = source.readString();
source.readTypedList(stopTitles, Stops.CREATOR);
}
public String getStation_name() {
return station_name;
}
public void setStation_name(String station_name) {
this.station_name = station_name;
}
public void setStopTitles(ArrayList<Stops> stopTitles) {
this.stopTitles = stopTitles;
}
public ArrayList<Stops> getStopTitles() {
return stopTitles;
}
#Override
public String toString() {
return station_name;
}
#Override
public int describeContents() {
// TODO Auto-generated method stub
return 0;
}
#Override
public void writeToParcel(Parcel dest, int arg1) {
// TODO Auto-generated method stub
dest.writeString(station_name);
dest.writeList(stopTitles);
}
public static final Parcelable.Creator<Stops> CREATOR = new Parcelable.Creator<Stops>() {
#Override
public Stops createFromParcel(Parcel in) {
return new Stops(in);
}
#Override
public Stops[] newArray(int size) {
return new Stops[size];
}
};
}

Have you made a Parcelable implementation of the Object you are holding in the ArrayList? If the object is not parcelable you cannot pass the list to onSavedInstance example code:
public class ParcelableObject implements Parcelable {
String field;
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeValue(field);
}
public static final Parcelable.Creator<ParcelableObject> CREATOR =
new Parcelable.Creator<ParcelableObject>() {
public ParcelableObject createFromParcel(Parcel in) {
ParcelableObject obj = new ParcelableObject();
obj.readFromParcel(in)
return obj;
}
public ParcelableObject[] newArray(int size) {
return new ParcelableObject[size];
}
};
private void readFromParcel(Parcel in) {
field = in.readString()
}
}

Related

ClassCastException when trying to use ParcelableArrayList on custom object array

I’m trying to implement Parcelable instead of Serializable as it’s supposed to be more efficient.
From my MainActivity I want to pass the object of generic type ArrayList<LogChartData> to the same activity, so that I get it on orientation change.
If I get past this one error I’m sure I’ll run in to a lot more of them, but right now when I try to run my app I’m getting,
Caused by: java.lang.ClassCastException: java.lang.String cannot be cast to java.util.Date
at com.testapp.util.LogChartData.<init>(LogChartData.java:60)
at com.testapp.util.LogChartData.<init>(LogChartData.java:59)
at com.testapp.util.LogChartData$1.createFromParcel(LogChartData.java:51)
at com.testapp.util.LogChartData$1.createFromParcel(LogChartData.java:1)
at android.os.Parcel.readParcelable(Parcel.java:2103)
at android.os.Parcel.readValue(Parcel.java:1965)
MainActivity.java--
ArrayList<LogChartData> logss = new ArrayList<LogChartData>();
#Override
public void onSaveInstanceState(final Bundle outState) {
outState.putParcelableArrayList("logss", logss);
outState.putParcelableArrayList("alert", alert);
outState.putBoolean("isRotate", true);
super.onSaveInstanceState(outState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
if (savedInstanceState != null) {
try
{
logss = savedInstanceState.getParcelableArrayList("logss");
alert = savedInstanceState.getParcelableArrayList("alert");
isRotate = savedInstanceState.getBoolean("isRotate");
}
catch(ClassCastException e)
{
e.printStackTrace();
}
if(!isRotate)
someFunctionCall();
// More code here..
}
}
LogCharData.java --
public class LogChartData implements Parcelable {
public Date chartDate = null;
public ArrayList<LogEntries> logs = new ArrayList<LogEntries>();
public LogChartData(Date chartDate, ArrayList<LogEntries> logs) {
super();
this.chartDate = chartDate;
this.logs = logs;
}
public LogChartData() {
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
}
public static final Parcelable.Creator<LogChartData> CREATOR =
new Parcelable.Creator<LogChartData>() {
public LogChartData createFromParcel(Parcel in) {
return new LogChartData(in);
}
public LogChartData[] newArray(int size) {
return new LogChartData[size];
}
};
private LogChartData(Parcel in) {
this.chartDate =
(Date)in.readValue(LogChartData.class.getClassLoader()); //Error Here..
in.readList(logs, LogChartData.class.getClassLoader());
}
public Date getChartDate() {
return chartDate;
}
public void setChartDate(Date chartDate) {
this.chartDate = chartDate;
}
public ArrayList<LogEntries> getLogs() {
return logs;
}
public void setLogs(ArrayList<LogEntries> logs) {
this.logs = logs;
}
}
You cannot Directly cast the String variable directly to the Date Object like this
(Date)in.readValue(LogChartData.class.getClassLoader());
You need to use SimpleDateFormat and parse the String from Specified DateFormat and then Stored in the Date Object
Suppose if your Date as the format of "yyyy-MM-dd"
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
this.chartDate= format.parse(in.readValue(LogChartData.class.getClassLoader()));
you have to use writeToParcel(Parcel dest, int flags) method. do something like this
public LogChartData(Parcel source) {
Parcelable[] parcelables = source.readParcelableArray(Thread
.currentThread().getContextClassLoader());
for (Parcelable parcelable : parcelables) {
logs.add((LogEntries) parcelable);
}
}
#Override
public void writeToParcel(Parcel dest, int flags) {
LogEntries[] data = new LogEntries[getLogs().size()];
for (int i = 0; i < data.length; i++) {
data[i] = getLogs().get(i);
}
dest.writeParcelableArray(data, flags);
}
I don't know about Date object. it is Parcelable or not . I think you have to convert it into string using SimpleDateFormat .
LogEntries should implement Parcelable interface as well
Suppose:
public class LogEntries implements Parcelable {
private String log;
public LogEntries() {}
public LogEntries(String log) {
this.log = log;
}
public String getLog() {
return log;
}
public void setLog(String log) {
this.log = log;
}
/*--- INIT parcelable code ---*/
#Override
public int describeContents() {
// TODO Auto-generated method stub
return 0;
}
public void readFromParcel(Parcel source) {
this.log = (String)source.readValue(String.class.getClassLoader());
}
/* Parceling constructor */
public LogEntries(Parcel source) {
this();
readFromParcel(source);
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeValue(log);
}
public static final Parcelable.Creator<LogEntries> CREATOR = new Parcelable.Creator<LogEntries>() {
#Override
public LogEntries createFromParcel(Parcel source) {
return new LogEntries(source);
}
#Override
public LogEntries[] newArray(int size) {
return new LogEntries[size];
}
};
/*--- END parcelable code ---*/
}
Then:
public class LogChartData implements Parcelable {
public Date chartDate = null;
public ArrayList<LogEntries> logs = new ArrayList<LogEntries>();
public LogChartData(Date chartDate, ArrayList<LogEntries> logs) {
super();
this.chartDate = chartDate;
this.logs = logs;
}
public LogChartData() {}
public Date getChartDate() {
return chartDate;
}
public void setChartDate(Date chartDate) {
this.chartDate = chartDate;
}
public ArrayList<LogEntries> getLogs() {
return logs;
}
public void setLogs(ArrayList<LogEntries> logs) {
this.logs = logs;
}
/*--- INIT parcelable code ---*/
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
if(chartDate!=null) {
dest.writeLong(chartDate.getTime());
}
else {
dest.writeLong(0);
}
if(logs!=null) {
dest.writeInt(logs.size()); // specify the number of logs
dest.writeTypedList(logs);
}
else {
dest.writeInt(-1); // specify there are no logs
}
}
public LogChartData(Parcel source) {
this();
readFromParcel(source);
}
#Override
public int describeContents() {
// TODO Auto-generated method stub
return 0;
}
public void readFromParcel(Parcel source) {
long timeMillis = source.readLong();
if(timeMillis!=0) {
this.chartDate = new Date(timeMillis);
}
else {
this.chartDate = null;
}
int logsSize = source.readInt();
if(logsSize>-1) {
this.logs = new ArrayList<LogEntries>(logsSize);
if(logsSize>0) {
source.readTypedList(logs, LogEntries.CREATOR);
}
}
else {
this.logs = null;
}
}
public static final Parcelable.Creator<LogChartData> CREATOR = new Parcelable.Creator<LogChartData>() {
#Override
public LogChartData createFromParcel(Parcel source) {
return new LogChartData(source);
}
#Override
public LogChartData[] newArray(int size) {
return new LogChartData[size];
}
};
/*--- END parcelable code ---*/
}
where you can use the long value from Date and retrieve it accordingly

How to extend android class which implements Parcelable interface?

First of all i have check this answer.
What i am trying to do is extending Location class calling it LocationPlus which has some
member variables. functionality i am trying to achieve is pass the object of LocationPlus class from one activity to another.
Here is my CREATOR
public static final Parcelable.Creator<LocationPlus> CREATOR = new Parcelable.Creator<LocationPlus>() {
#Override
public LocationPlus createFromParcel(Parcel source) {
return new LocationPlus(source);
}
#Override
public LocationPlus[] newArray(int size) {
return new LocationPlus[size];
}
};
problem i am facing is this error
Implicit super constructor Location() is undefined. Must explicitly invoke another constructor
when trying to write constructor
public LocationPlus(Parcel in) {
Someone in comment ask me to post LocationPlus class so here it is
public class LocationPlus extends Location{
private int mBattery = -1;
public LocationPlus(String locationName) {
super(locationName);
}
public LocationPlus(Location location) {
super(location);
}
public int getmBattery() {
return mBattery;
}
public void setmBattery(int mBattery) {
this.mBattery = mBattery;
}
#Override
public int describeContents() {
return 0;
}
public static final Parcelable.Creator<LocationPlus> CREATOR = new Parcelable.Creator<LocationPlus>() {
#Override
public LocationPlus createFromParcel(Parcel source) {
return new LocationPlus(source);
}
#Override
public LocationPlus[] newArray(int size) {
return new LocationPlus[size];
}
};
#Override
public void writeToParcel(Parcel out, int flags) {
super.writeToParcel(out, flags);
out.writeInt(mBattery);
}
public LocationPlus(Parcel in) {
mBattery =in.readInt();
}
}
Parcelable, the Speed King
According to google engineers, this code will run significantly faster. One of the reasons for this is that we are being explicit about the serialization process instead of using reflection to infer it. It also stands to reason that the code has been heavily optimized for this purpose.
public abstract class BaseClass implements Parcelable {
public String FullName;
public boolean IsValidUser;
public String UserName;
public BaseClass () {
}
protected BaseClass(Parcel in) {
FullName = in.readString();
IsValidUser = in.readByte() != 0;
UserName = in.readString();
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(FullName);
dest.writeByte((byte) (IsValidUser ? 1 : 0));
dest.writeString(UserName);
}
}
Child class will be as follows with usage of list adding into parcelable object:
public class DerivedClass extends BaseClass {
public boolean IsSuccess;
public String Message;
public List<AnotherClass> AnotherClassObj;
public DerivedClass () {
super();
}
protected DerivedClass(Parcel in) {
super(in);
AnotherClassObj = new ArrayList<AnotherClass>();
IsSuccess = in.readByte() != 0;
Message = in.readString();
AnotherClassObj = in.readArrayList(AnotherClass.class.getClassLoader());
}
public static final Creator<DerivedClass> CREATOR = new Creator<DerivedClass>() {
#Override
public DerivedClass createFromParcel(Parcel in) {
return new DerivedClass(in);
}
#Override
public DerivedClass[] newArray(int size) {
return new DerivedClass[size];
}
};
#Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeByte((byte) (IsSuccess ? 1 : 0));
dest.writeString(Message);
dest.writeList(AnotherClassObj);
}
public int describeContents() {
return 0;
}
}
Another child class :
public class AnotherClass extends BaseClass {
public AnotherClass() {
super();
}
protected AnotherClass(Parcel in) {
super(in);
}
public int describeContents() {
return 0;
}
public static final Creator<AnotherClass> CREATOR = new Creator<AnotherClass>() {
#Override
public AnotherClass createFromParcel(Parcel in) {
return new AnotherClass(in);
}
#Override
public AnotherClass[] newArray(int size) {
return new AnotherClass[size];
}
};
#Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
}
}
In Activity:
Intent intent = new Intent(LoginActivity.this, MainActivity.class);
intent.putExtra("UserObject", parcelableObject);
startActivity(intent);
finish();
In receiving activity:
Bundle extras = getIntent().getExtras();
if (extras != null) {
userObject = extras.getParcelable("UserObject");
}
Hi I've do research a lot about this, but I couldn't find useful anything. I try solution below and it worked for me.
Let say your super class has only int variable named "mData".
public class Location implements Parcelable {
protected int mData;
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel out, int flags) {
out.writeInt(mData);
}
public static final Parcelable.Creator<Location> CREATOR
= new Parcelable.Creator<Location>() {
public Location createFromParcel(Parcel in) {
return new Location(in);
}
public Location[] newArray(int size) {
return new Location[size];
}
};
private Location(Parcel in) {
mData = in.readInt();
}
}
Then, your extended class has only int variable named "mBattery".
public class LocationPlus extends Location {
protected int mBattery;
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel out, int flags) {
out.writeInt(mBattery);
}
public static final Parcelable.Creator<LocationPlus> CREATOR
= new Parcelable.Creator<LocationPlus>() {
public LocationPlus createFromParcel(Parcel in) {
return new LocationPlus(in);
}
public LocationPlus[] newArray(int size) {
return new LocationPlus[size];
}
};
private LocationPlus(Parcel in) {
mBattery = in.readInt();
}
}
So far, LocationPlus works fine. But we don't set variable of super class. Firstly, I set super class' variables on extended class with super(..) method. But it didn't work.
private LocationPlus(Parcel in) {
super(in);
mBattery = in.readInt();
}
Instead of code above, you should set all super class' variables explicitly. Super class' variables should be protected. Final constructor should be like this:
private LocationPlus(Parcel in) {
mData = in.readIn();
mBattery = in.readInt();
}
and writeToParcel method should be like this:
public void writeToParcel(Parcel out, int flags) {
out.writeIn(mData);
out.writeInt(mBattery);
}
Try this solution:
public static final Parcelable.Creator<LocationPlus> CREATOR =
new Parcelable.Creator<LocationPlus>() {
#Override
public LocationPlus createFromParcel(Parcel in) {
Location l = Location.CREATOR.createFromParcel(in);
LocationPlus lp = new LocationPlus(l);
lp.mBattery= in.readInt();
return lp;
}
#Override
public LocationPlus[] newArray(int size) {
return new LocationPlus[size];
}
};
#Override
public void writeToParcel(Parcel parcel, int flags) {
super.writeToParcel(parcel, flags);
parcel.writeInt(mBattery);
}
According to the Android docs, there isn't a Location() constructor for the Location class. When initializing your LocationPlus class, you need to call either super(String provider) or super(Location l).
Edit: Corrected syntax
(See Location Android Doc)

How to implement parcelable for List<Long>

I'm trying to pass a List in my parcelable doing:
public class MetaDados implements Parcelable {
private List<Long> sizeImages;
public MetaDados(List<Long> sizeImages){
this.sizeImages = sizeImages;
}
public List<Long> getSizeImages() {
return sizeImages;
}
public void setSizeImages(List<Long> sizeImages) {
this.sizeImages = sizeImages;
}
#Override
public int describeContents() {
// TODO Auto-generated method stub
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeList(sizeImages);
}
public static final Parcelable.Creator<MetaDados> CREATOR = new Parcelable.Creator<MetaDados>() {
#Override
public MetaDados createFromParcel(Parcel in) {
return new MetaDados(in);
}
#Override
public MetaDados[] newArray(int size) {
return new MetaDados[size];
}
};
private MetaDados(Parcel in) {
//HERE IS THE PROBLEM, I'VE tried this:
sizeImages = in.readList(sizeImages, Long.class.getClassLoader());
//but what i got: Type mismatch: cannot convert from void to List<Long>
}
}
Try this instead:
sizeImages = new ArrayList<Long>(); // or any other type of List
in.readList(sizeImages, null);
The Android documentation for Parcel.readList says:
Read into an existing List object from the parcel
and thus, you need to first create the List.

Implementing Parcelable

I have implemented parcelable in many of my classes that only have String member variables. I am not sure how to do it for a class that has an ArrayList. What do I do in the constructor that takes in the source (Parcel)?
public class Schedule {
private ArrayList<Event> allEvents;
public Schedule(Parcel source) {
}
public Schedule(){
allEvents = new ArrayList<Event>();
}
//Getter
public ArrayList<Event> getAllEvents(){
return allEvents;
}
public void addEvent(Event newEvent){
allEvents.add(newEvent);
}
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel dest, int flags) {
dest.writeList(allEvents);
}
public static final Parcelable.Creator<Schedule> CREATOR =
new Parcelable.Creator<Schedule>() {
#Override
public Schedule createFromParcel(Parcel source) {
return new Schedule(source);
}
#Override
public Schedule[] newArray(int size) {
return new Schedule[size];
}
};
}

Android Parcelable Problem with array

Hi everybody i have build a class that implements Parcelable but one of the arraylist attributes i have define gets empty when i read the class. Here is the code
package roblestech.laCartelera;
import java.util.ArrayList;
import android.os.Parcel;
import android.os.Parcelable;
public class ProgramacionPelicula implements Parcelable {
public ProgramacionPelicula() {
}
public ProgramacionPelicula(Pelicula pelicula) {
_pelicula = pelicula;
}
public ProgramacionPelicula(Cine cine) {
_cine = cine;
}
public String toString() {
if (getVista() == ProgramacionPelicula.VISTA_PELICULA) {
return getCine().getCine();
} else {
return getPelicula().getTituloOriginal();
}
}
private int _idProgramacion;
public void setIdProgramacion(int value) {
_idProgramacion = value;
}
public int getIdProgramacion() {
return _idProgramacion;
}
private Pelicula _pelicula;
// public ArrayList<Pelicula> _peliculas = new ArrayList<Pelicula>();
public void setPelicula(Pelicula pelicula) {
_pelicula = pelicula;
}
public Pelicula getPelicula() {
return _pelicula;
}
private Cine _cine;
public void setCine(Cine cine) {
_cine = cine;
}
public Cine getCine() {
return _cine;
}
public ArrayList<Tanda> _tandas = new ArrayList<Tanda>();
public void setTandas(ArrayList<Tanda> value) {
_tandas = value;
}
public void setTandas(Object[] tandas) {
for (Object tanda : tandas) {
if (tanda instanceof Tanda) {
_tandas.add((Tanda) tanda);
}
}
}
public void addTanda(Tanda value) {
_tandas.add(value);
}
public ArrayList<Tanda> getTandas() {
return _tandas;
}
private String _sala = "";
public void setSala(String value) {
_sala = value;
}
public String getSala() {
return _sala;
}
public static final int VISTA_CINE = 0;
public static final int VISTA_PELICULA = 1;
private int _vista = VISTA_CINE;
public int getVista() {
return _vista;
}
public ProgramacionPelicula toPelicula() {
ProgramacionPelicula programacionPelicula = new ProgramacionPelicula();
programacionPelicula._idProgramacion = _idProgramacion;
programacionPelicula._pelicula = _pelicula;
programacionPelicula._cine = _cine;
programacionPelicula._tandas = _tandas;
programacionPelicula._sala = _sala;
programacionPelicula._vista = VISTA_PELICULA;
return programacionPelicula;
}
#Override
public int describeContents() {
// TODO Auto-generated method stub
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(getIdProgramacion());
dest.writeString(getSala());
ArrayList<Pelicula> peliculas = new ArrayList<Pelicula>();
peliculas.add(getPelicula());
Object[] objectsPeliculas = peliculas.toArray();
dest.writeArray(objectsPeliculas);
Object[] objectsTanda = getTandas().toArray();
dest.writeArray(objectsTanda);
}
// this is used to regenerate your object. All Parcelables must have a
// CREATOR that implements these two methods
public static final Parcelable.Creator<ProgramacionPelicula> CREATOR = new Parcelable.Creator<ProgramacionPelicula>() {
public ProgramacionPelicula createFromParcel(Parcel in) {
return new ProgramacionPelicula(in);
}
public ProgramacionPelicula[] newArray(int size) {
return new ProgramacionPelicula[size];
}
};
// example constructor that takes a Parcel and gives you an object populated
// with it's values
private ProgramacionPelicula(Parcel in) {
this();
setIdProgramacion(in.readInt());
setSala(in.readString());
Object[] obj = in.readArray(Pelicula.class.getClassLoader());
setPelicula((Pelicula) obj[0]);
setTandas(in.readArray(Tanda.class.getClassLoader()));
}
}
Thanks in advance everybody.
Another way to pass an array would be using typed arrays as follows:
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mMyArray.length);
dest.writeTypedArray(mMyArray, flags);
}
And to read it back like this:
public MyParcelableObject(Parcel in) {
mMyArray = new MyOtherParcelableObject[in.readInt()];
in.readTypedArray(mMyArray, MyOtherParcelableObject.CREATOR);
}
In short, write the length of the array as an int value to the parcelable, right before writing its values, so you can create an array of the same size before reading it.
In order to read/write a custom class property you need to use writeParcelable and readParcelable in stead of writing it into an array.
Change your parcel constructor to:
private ProgramacionPelicula(Parcel in) {
this();
setIdProgramacion(in.readInt());
setSala(in.readString());
_pelicula = in.readParcelable(Pelicula.class.getClassLoader());
setTandas(in.readArray(Tanda.class.getClassLoader()));
}
And your writeToParcel method to:
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(getIdProgramacion());
dest.writeString(getSala());
dest.writeParcelable(_pelicula, flags);
Object[] objectsPeliculas = peliculas.toArray();
dest.writeArray(objectsPeliculas);
Object[] objectsTanda = getTandas().toArray();
dest.writeArray(objectsTanda);
}
If you really want to use an array, you must invoke the readTypedArray/writeTypedArray methods. Like so:
#Override
public void writeToParcel(Parcel dest, int flags) {
ArrayList<Pelicula> list = new ArrayList<Pelicula>();
list.add(_pelicula);
dest.writeTypedList(list);
}
private ProgrammacionPelicula(Parcel in) {
ArrayList<Pelicula> list = new ArrayList<Pelicula>();
in.readTypedList(list, ProgrammacionPelicula.CREATOR);
}
And you should do the same for your Tanda properties.
Hope this helps.
The most concise, write like this:
public void writeToParcel(Parcel dest, int flags) {
dest.writeTypedArray(this.mMyArray, flags);
}
and read like this:
public MyParcelableObject(Parcel in) {
this.mMyArray = in.createTypedArray(MyOtherParcelableObject.CREATOR);
}

Categories

Resources