In my code I created a card class; I show a bunch of cards later in a gridview.
On screen orientation change I am loosing all the cards; with my previous question I was pointed in the right direction.
Now, what I have found in Android documentation and here in StackO is that
#Override
protected void onSaveInstanceState(Bundle outState) {
outState.putInt(...);
super.onSaveInstanceState(outState);
}
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
some_int_var = savedInstanceState.getInt(...);
super.onRestoreInstanceState(savedInstanceState);
}
Now, I do that OK, since the Bundle object has several methods like putString, Char, etc. The primitives plus string that is. But what about my cards? they are Card objects in a vector thus I can't use any of those methods.
How can I restore that vector WITHOUT using onRetainNonConfigurationInstance nor preventing the activity reset? In Android documentation it is advice to do this if there's heavy data to restart but that's not my case.
For your own objects you can use putParcelable()
To make your object parcelable you should implement Parcelable and follow the following example to implement it.
http://prasanta-paul.blogspot.nl/2010/06/android-parcelable-example.html
So:
public class ParcelData implements Parcelable {
String name;
ParcelData (Parcel source) {
name = source.readString();
}
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
}
public class MyCreator implements Parcelable.Creator<ParcelData> {
public ParcelData createFromParcel(Parcel source) {
return new ParcelData(source);
}
public ParcelData[] newArray(int size) {
return new ParcelData[size];
}
}
}
first of all - you can pass with intent complex objects.
the why to do that is to make your class implement Serializable interface, or Parcelable,
and then use the intent.getSerializableExtra(keyName); or intent.getParcelableExtra();
Serializable is the easier to implement (basically declaring your class as implementing it is enough) but some specific classes cannot be serialized, while implementing Parcelable require a bit more work, but always possible.
other option - is holding the data you need to save in some singeltone class with getters and setters for save what ever you'd like to save, and use it anytime and anywhere in your code:
saving your data in the onSaveInstanceState method, and getting it back in onRestoreInstanceState method.
Related
What is the correct way of implementing the Parcelable interface in Android? According to the documentation you should implement the writeToParcel method and have a CREATOR.
public void writeToParcel(Parcel out, int flags) {
out.writeInt(mData);
}
But when I implement it without adding a CREATOR and leaving the writeToParcel() empty the app still seems to work correctly. Sometimes I would get a Bad Parcelable Exception but I can't work out the steps to replicate.
This is how I use to pass an object from activity to fragment
Bundle bundle = new Bundle();
bundle.putParcelable(PageFragment.PAGE_FILTER_KEY, page);
fragment.setArguments(bundle);
So, what is the purpose of adding stuff like out.writeInt(mData); what kind of problems can be expected if this is not done?
Parcelable implementation mainly have two process steps.
1 Writing your java object to Parcel which includes two methods.
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(cityName);
dest.writeString(macroName);
dest.writeString(id);
}
where describe content is for setting a flag for your contents. Most of time you just need to it untouched.
public void writeToParcel(Parcel dest, int flags) , you need to write you Java class object to parcel step by step according to fields in JAVA class. In above example my class has three strings. You can write almost all types of objects in parcel. You just need to chose appropriate one. Like writeString(),writeList() or writeObject() etc.
2. Second part is reading your java object back from parcel
This part required two things as well. First is CREATOR of your java class like following
public static final Creator<City> CREATOR = new Creator<City>() {
#Override
public City createFromParcel(Parcel in) {
return new City(in);
}
#Override
public City[] newArray(int size) {
return new City[size];
}
};
In above example my Java class is City. It makes read a City object from parcel. But it calls new City(in) constructor of City class. So now I need a constructor which accept a parcel object in arguments. Lets create that too..
protected City(Parcel in) {
cityName = in.readString();
macroName = in.readString();
id = in.readString();
}
Now we make a class complete full proof parcelable. One thing to notice, we need to read members in same sequence at protected City(Parcel in) we put them in parcel i.e. in writeToParcel() method.
On how to reproduce badParcelable exeption in simply letting android create java object from parcelable. For that you can choose Destroy activities from developer options on android device and put you app in background in that activity, so android kill your application process ID. Resume your app by recreating activity (onCreate + Bundle), you will get that exception if you does not implemented parcelable correctly.
But when I implement it without adding a CREATOR and leaving the writeToParcel() empty the app still seems to work correctly.
CREATOR is used when reading data back out of a Parcel and converting it back into objects.
writeToParcel() puts your data into the Parcel.
The only way that leaving those off will work correctly is in cases where your Parcelable is not actually being put into a Parcel or reconstituted from a Parcel. Examples include LocalBroadcastManager.
what is the purpose of adding stuff like out.writeInt(mData);
It would be the same purpose as adding stuff like out.write() with an OutputStream: it writes to the output. Your question is akin to asking "hey, if I don't write data to my file, what sorts of problems will I encounter?".
I happen to know what if my class Info which implements Parcelable has empty writeToParcel() method, Bundle.putParcelableArrayList() and Bundle.getParcelableArrayList() methods still work on ArrayList< Info>.
public class Info implements Parcelable
{
public int row;
public int column;
public int describeContents()
{
return 0;
}
public void writeToParcel(Parcel out, int flags)
{
// Left empty!
}
}
So the question is why? When I really should implement writeToParcel method as API documentation and books instruct?
The Android Bundle class does not follow the same protocol that is followed during IPC marshaling. Through reflection, a class that implements Bundle will simply read & write the Parcelable object into its own internal mapping. So, whatever properties you define in your Parcelable derived class will be used.
On the contrary, if you're using the IPC Marshalling protocol - e.g. passing your Info object from one activity to another - this is when you need to implement a specific writeToParcelable method and the appropriate constructor.
I'm trying to come to grips with the onSaveInstanceState method in class View (not the one in class Activity). That method does return a Parcelable. I derived my own View from ViewGroup and overrode that method to save my own state. But when the state was to be saved I got an exception:
java.lang.IllegalStateException:
Derived class did not call super.onSaveInstanceState()
That is true enough, but simply calling that method doesn't seem enough to me. So how should I do this? If the method would get passed a Parcel to write to, I could simply pass that same parcel to the super class, so things would get written sequentially. But this is a return value.
Should I include this returned object as a member of my own Parcelable representation, and use Parcel.writeParcelable to marshal it along with my own data if needed? Or is there some better way to handle parent invocation and chaining of parcelable objects? If so, what class loader should I use when loading the instance state of the super class?
Since zapl didn't turn his comment into an answer, I'm doing so.
is there some better way to handle parent invocation and chaining of parcelable objects?
The canonical way to accomplish this is by having your own class for saved data derived from View.BaseSavedState, which in turn is derived from AbsSavedState. You can call the onSaveInstance handler of the parent class and pass the resulting object to the constructor of your own class. When restoring the data, getSuperState gives the instance aimed at the parent class.
A typical code example could look like this:
static class SavedState extends View.BaseSavedState {
// include own data members here
public SavedState(Parcelable superState) {
super(superState);
}
private SavedState(Parcel in) {
super(in);
// read own data here
}
#Override public void writeToParcel(Parcel out, int flags) {
super.writeToParcel(out, flags);
// write own data here
}
public static final Parcelable.Creator<SavedState> CREATOR =
new Parcelable.Creator<SavedState>() {
public SavedState createFromParcel(Parcel in) { return SavedState(in); }
public SavedState[] newArray(int size) { return new SavedState[size]; }
};
}
#Override public Parcelable onSaveInstanceState() {
SavedState state = new SavedState(super.onSaveInstanceState());
// set data members here
return state;
}
#Override public void onRestoreInstanceState(Parcelable parcelable) {
SavedState state = (SavedState)parcelable;
super.onRestoreInstanceState(state.getSuperState());
// restore from data members here
}
The above was adapted from this presentation by Cyril Mottier, but should also be a close match to how designers intended the use of this class in general.
Should I include this returned object as a member of my own Parcelable representation, and use Parcel.writeParcelable to marshal it along with my own data if needed?
Although the mentioned described above seems to be preferred, behind the scenes it does rely on writeParcelable as well. So if there are reasons to not use that base class, simply calling writeParcelable to store the state of the super class should be fine.
what class loader should I use when loading the instance state of the super class?
The current implementation of AbsSavedState does use null as the class loader argument, causing the use of the default class loader. However, that line of code is marked with a FIXME comment, so it might change one day.
I'm asking this question: instread of giving a string, a int and so on, can we push a custom object during the creation fo a new Intent?
newActivity.PutExtra("JsonDataResult", business.getJSON());
In fact I have one object constructed thanks to a JSON (from webrequest) , I parse it and I put it on an object.
At this point I'm passing the string returned from the webrequest to another intent but the parsing takes a long time tu be done, so it could be super-cool the ability to pass custom object with intent.
EDIT : I'm using monodroid / xamarin, so
Android.OS.IParcelable cannot be implemented,
Java.IO.ISerializable cannot be implemented.
You can either let your custom classes implement Parcelable (Google says its faster, but you have to do more coding) or Serializable.
Then add your objects to a bundle (or to the "extra"):
Bundle b = new Bundle()
b.putParcelable("myObject",myObject);
b.putSerializable("myObject",myObject);
For info to Parcelablecheckout this
And if you're interested in the difference between Parcelable and Serializable in more detail check out this
I personally prefer the usage of Serializable for simple object-passing, since the code ist not spoiled with so much code.
Edit: ok isn't your question very similar to this then?
As you've specified you're using Monodroid, it looks like it's not straightforward. I did a quick search and found this forum post
Which listed the following solutions to this problem in Monodroid:
Store the custom Object to be passed as a global variable somewhere, and just read it from your second activity
Which is a bit messy and bad practice, but would work.
Or
serialize your class to a string and send the string to the second Activity
Which will be a little more hard work, but better practice
This is an example how to create a Parcelable class:
public class Person implements Parcelable {
private String name;
private String surname;
private String email;
// Get and Set methods
#Override
public int describeContents() {
return hashCode();
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeString(surname);
dest.writeString(email);
}
// We reconstruct the object reading from the Parcel data
public Person(Parcel p) {
name = p.readString();
surname = p.readString();
email = p.readString();
}
public Person() {}
// We need to add a Creator
public static final Parcelable.Creator<person> CREATOR = new Parcelable.Creator<person>() {
#Override
public Person createFromParcel(Parcel parcel) {
return new Person(parcel);
}
#Override
public Person[] newArray(int size) {
return new Person[size];
}
};
Give a look here if you want to use Parcelable.
My program has a range of different class activities (basically different screens). In one activity I am creating multiple objects which I would then like to access in other activities.
How do I go about making these objects accessible to other activities within my program, in other words how do I share objects with other activities?
TIA
Mark
The first thing you need to resolve is the operation order. If activity A is the one with the shared objects, what would you do if activity B is run without activity A ever being initialized? Do remember that intents to start activities may come from everywhere, though, to be truthful, exiting with NULL pointer dereference is an acceptable response.
What I did when such a thing was necessary was to not have the shared objects part of the activity, but create a specific object for containing those. You can then store a static reference to that object inside the object, and return it via a static method:
public class GlobalParams {
private static reference;
public static GlobalParams getReference()
{
if( reference==NULL )
reference=new GlobalParams();
return reference;
}
}
I don't think parcelable would help you, as that would create distinct copies for the different Activities to use.
Shachar
You need to have that class implement Parcelable
It's basically kinda similar to Java's serializable. You have to tell your class how to pack and unpack itself. Then you can just put it in an intent via intent.putExtra();
Here is the code example taken from that link
public class MyParcelable implements Parcelable {
private int mData;
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel out, int flags) {
out.writeInt(mData);
}
public static final Parcelable.Creator<MyParcelable> CREATOR
= new Parcelable.Creator<MyParcelable>() {
public MyParcelable createFromParcel(Parcel in) {
return new MyParcelable(in);
}
public MyParcelable[] newArray(int size) {
return new MyParcelable[size];
}
};
private MyParcelable(Parcel in) {
mData = in.readInt();
}
}