Should I implement writeToParcel method? - android

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.

Related

Android Parcelable: reading and writing

I want to make a custom entity class Parcelable.. I have some fields in it: a String[] and another custom entity object (which is parcelable).. I want to know how to read and write these objects and lists..
public class CustomEntity implements Parcelable {
private int number;
private String[] urls;
private AnotherEntity object;
public CustomEntity(Parcel in) {
number = in.readInt();
// how should I read urls?
// how should I read object?
}
#Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeInt(number);
// how should I write urls?
// how should I write object?
}
}
For a String[] you can use the API
parcel.writeStringArray(url)
For AnotherEntity you need to extend it with Parcelable again
parcel.writeParcelable();
https://github.com/mcharmas/android-parcelable-intellij-plugin use this plugin!
Your AnotherEntity must implemented Parcelable too!
I definitely think you should NOT handle the boilerplate yourself.
There are libraries around like Parceler where with only one annotation on your POJO and one line like Parcel.wrap or Parcel.unwrap you can do instant serialization.

Overriding onSaveInstanceState

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.

Recover an object on screen orientation change

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.

Extending a class that implements Parcelable

I have a class, we'll call it class A, that implements Parcelable.
I have a second class, we'll call it class B, that extends class A.
My question is:
How do I write class B's member variables to the Parcel and then write it's parent class's (ie: class A's) member variables to the Parcel (and, subsequently, read them in)?
Is there some nifty trick to not needing to rewrite class A's Parcel code? Or do I just need to rewrite the Parcel code in class A and add additional code for class B's member variables?
How do I write class B's member variables to the Parcel and then write it's parent class's (ie: class A's) member variables to the Parcel
Class B overrides writeToParcel() from Class A, chaining to the superclass and also adding its own objects to the Parcel.
(and, subsequently, read them in)?
Class B implements public static final Parcelable.Creator<MyParcelable> CREATOR in such a way that it can let both classes read their stuff in. If you take the approach of creating a constructor on Class B that takes a Parcel as a constructor parameter, just chain to the superclass constructor (to let Class A do its work), then read Class B's data.
The key will be to do them both in the same order. If you intend to let Class A read its data first, Class A must write its data first.
Is there some nifty trick to not needing to rewrite class A's Parcel code?
Inheritance and chaining to the superclass.
Adding an example, the marked answer is indeed correct, but something more visual seems more suitable for this situation:
This would be the supper class:
public class BasePojo implements Parcelable {
private String something;
//what ever other constructor
//getters and setters
protected BasePojo(Parcel in) {
something = in.readString();
}
public static final Creator<BasePojo> CREATOR = new Creator<BasePojo>() {
#Override
public BasePojo createFromParcel(Parcel in) {
return new BasePojo(in);
}
#Override
public BasePojo[] newArray(int size) {
return new BasePojo[size];
}
};
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeString(something);
}
}
And then this would be the child class:
public class ChildPojo extends BasePojo implements Parcelable {
private int somethingElse;
//what ever other constructor
//getters and setters
protected ChildPojo(Parcel in) {
super(in);
somethingElse = in.readInt();
}
public static final Creator<ChildPojo> CREATOR = new Creator<ChildPojo>() {
#Override
public ChildPojo createFromParcel(Parcel in) {
return new ChildPojo(in);
}
#Override
public ChildPojo[] newArray(int size) {
return new ChildPojo[size];
}
};
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel parcel, int i) {
super.writeToParcel(parcel, i);
parcel.writeInt(somethingElse);
}
}
The marked answer provides a very good explanation, calling super is the key.
It is a little complex, but the trick is to use Reflection to get the types of subclass's members and to sort the members so that you can read and write the data back in the same exact order using the proper types.
I have implemented the solution for class A here: https://github.com/awadalaa/Android-Global-Parcelable
so now you can make any class parcelable by simply extending this class.

Android Parcelable and Serializable

So i know it is recommended to use Parcelable instead of Serializable in android, because it is faster.
My question is: is that impossible to avoid using Serializable right?
If I have a custom object i want to serialize, let's say I have the following class definition
public class Person {
String name;
int Age;
...
....
}
Making this parcelable is easy, because the Person class contains the types parcel.write*() supports, i.e. there is parcel.writeString and parcel.writeInt
Now, what if the Person class is the following:
public class PersonTwo {
MyCustomObj customObj;
String name;
int Age;
...
....
}
How am I suppose to parcel the MyCustomObj object??
It seems I need to use serializable again? but again, I thought it is SLOW to use serializable, and seems we have no choice but to use it in this case.
I don't understand
can someone tell me how I would parcel PersonTwo in this case?
The link given by Ajay is the exact what you are looking for, how you can do it.
Well, what you can do is implement Parcelable to your CustomObject1 and create a Parcelable class for it and then you can use that Parcelable class to Parcel it inside another Parcelable class that will Parcel both the CustomObjects.
public class CustomObject1 implements Parcelable {
// parcelable code CustomObject1
}
public class CustomObject2 implements Parcelable {
private CustomObject1 obj1;
// add CustomObject1 here with getter setter
// parcelable code for CustomObject2
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeParcelable(obj1, flags);
}
private void readFromParcel(Parcel in) {
obj1 = in.readParcelable(CustomObject1.class.getClassLoader());
}
............
}
You need to make MyCustomObj parcelable.
All the composite objects should also be Parcelable. In case, you want to skip an object then don't use it writeToParcel method.
I came to point where Parcelable is an issue for me.
On Android 4.3, I am getting unmarhalling exception, when passing data between
Activities as Parcelable. It works OK on Android 4.0, 4.2 or 4.4.
It should work when changed to Serializable, even though, it is slower.

Categories

Resources