Parcelable and Inheritance - android

This has been asked a few times here on SO, but my case is a bit different.
I have class A that implements Parcelable. Class A contains some member data that can be parceled. It has its own CREATOR and implements writeToParcel(), describeContents(), and a constructor that accepts a Parcel.
There is class B that extends from class A. Class B has additional member data, but none of them need to be parceled. Basically, class B's parcelable data is the same as class A. If I try to put B in a Bundle, pass it to another Activity, and read it back, I would get a ClassCastException. I guess that's expected.
After a bit of trial-and-error, in order to make class B parcelable, I have to implement at least these two things:
public static final Parcelable.Creator<B> CREATOR
= new Parcelable.Creator<B>() {
public B createFromParcel(Parcel source) {
return new B(source);
}
public B[] newArray(int size) {
return new B[size];
}
};
public B(Parcel in) throws JSONException {
super(in);
}
So my concern is this. There are about half a dozen or more classes that extend from A and all have the same issue as B. It seems silly that each one of them has to add their own static CREATOR and a constructor that accepts a Parcel, only to pass it back to A. Everything else is identical. The only thing that makes it different is the name of the class. It beats the purpose of having inheritance in the first place.
For example, if there's another class C that extends B, I need to do the same:
public static final Parcelable.Creator<C> CREATOR
= new Parcelable.Creator<C>() {
public C createFromParcel(Parcel source) {
return new C(source);
}
public C[] newArray(int size) {
return new C[size];
}
};
public C(Parcel in) throws JSONException {
super(in);
}
Is there some sort of clever techniques in Java to automate this process? Perhaps using generic of some sort? If there's no other way, I might just as well just remove the inheritance lineage, and require each class to implement Parcelable themselves.

This is a little complicated, but the only way I can think of offhand involves reflection - Provided all of the subclasses have a constructor that takes a Parcel that then calls super(parcel), you could make the class name a part of the parcel - then in your createFromParcel method of A:
public A createFromParcel(Parcel source) {
Class clazz = Class.forName(source.readString());
Class[1] paramTypes = { Parcel.class };
Constructor ctor = clazz.getConstructor(paramTypes);
A myA = (A) ctor.newInstance(source);
return myA;
}
Note that this was written largely off the cuff and may need some tweaking before it runs (I know for sure it's missing checked exception handlers) - but hopefully the idea is clear

I have implemented the solution described by JRaymond and it works. It's a little complicated since it involves some reflection,but i hope it helps someone else. Now any class that is parcelable should extend this GlobalParcelable class which implements parcelable.
https://github.com/awadalaa/Android-Global-Parcelable

Related

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.

Extending an Android Parcelable class

I have a CustomAddress class that extends the android.location.Address class that implements Parcelable.
I am trying to make my CustomAddressimplement Parcelableto but am stuck when creating my class from a parcel. What I want to when creating CustomAddressfrom a parcel is first fill in all the fields from the super class Addressand then my own fields. So I have implemented the CREATORfield:
public static final Parcelable.Creator<CustomAddress> CREATOR
= new Parcelable.Creator<CustomAddress>() {
public CustomAddress createFromParcel(Parcel in) {
return new CustomAddress(in);
}
public CustomAddress[] newArray(int size) {
return new CustomAddress[size];
}
};
But in my CustomAddress(Parcel in)creator, I can't call super(in)because it doesn't exist in android.location.Address. I can only access android.location.Address.CREATOR. So how do I fill in my fields using CREATOR?
EDIT: link to the android Address class https://developer.android.com/reference/android/location/Address.html
Here is a similar question and Mark Murphy's excellent answer: https://stackoverflow.com/a/10841502/1140682
So, in your case, you would make CustomAddress extend Address (as you already do), call the super() method in the constructor and then read your own attributes from the passed Parcel. Same has to be done (in same order) in the writeToParcel() method, of course here adding your attributes to the parcel.

How to share objects with other activities?

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();
}
}

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.

Parcel vs. Serializeable for exchanging objects among services/activities

Let me first give you the whole picture: I am developing a location based application, that invokes a constant recreation and exchange of objects, among several activities and services. The necessary data to create the objects being exchanged, is stored in an SQLite database, that in its turn gets populated by retrieving data from a remote SQL database.
It got quickly obvious, that passing raw attributes of an object to activities/services (via intents), was a huge coding overhead, diminishing every extension prospects the application might have. So, soon i decided to extend my main object class to implement a Parcelable one, as shown below:
public class MyProduct implements Parcelable {
//MyProduct Attributes
private int myProductId;
private String myProductDescription;
private float myProductRadius;
//More attributes...
public MyProduct() {
myProductId=-1;
myProductDescription="defaultProductDescription";
myProductRadius=10;
//More attributes
}
public int describeContents(){
return 0;
}
// write your object's data to the passed-in Parcel
public void writeToParcel(Parcel out, int flags){
//Product attributes
try{
out.writeInt(myProductId);
out.writeString(myProductDescription);
out.writeFloat(myProductRadius);
//More attributes
}
catch (Exception e){}
}
// this is used to regenerate your object. All Parcelables must have a CREATOR that implements these two methods
public static final Parcelable.Creator<MyProduct> CREATOR = new Parcelable.Creator<MyProduct>() {
//public class MyCreator implements Parcelable.Creator<MyProduct> {
public MyProduct createFromParcel(Parcel in) {
return new MyProduct(in);
}
public MyProduct[] newArray(int size) {
return new MyProduct[size];
}
};
// example constructor that takes a Parcel and gives you an object populated with it's values
private MyProduct(Parcel in) {
//in.readParcelable(MyProduct.class.getClassLoader());
try{
//MyProduct.class.getClassLoader();
myProductId=in.readInt();
myProductDescription=in.readString();
myProductRadius=in.readFloat();
//More attributes
}
catch(Exception e){}
}
//Setters and Getters
}//endOfMyProduct.class
Although i checked every data entry to the parcel fields, the following exception keeps spawning:
01-05 19:35:11.570: ERROR/Parcel(59): Class not found when unmarshalling: com.nifo.distribution.MyProduct, e: java.lang.ClassNotFoundException: com.nifo.distribution.MyProduct
For this reason, i consider of the MyProduct.class implementing serializable, hoping that it will turn to be a more error-forgiving structure. What would be the pros and cons of such alternation in the case described above?

Categories

Resources