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.
Related
I am using AutoValue in my models, I want to update the isTrue() value of the model when the user does something. So I need help. Here is my model.
#AutoValue
public abstract class Xyz implements Parcelable {
#SerializedName("isTrue")
public abstract boolean isTrue();
#Nullable
#SerializedName("lead_image_url")
public abstract String lead_image_url();
public static TypeAdapter<Readable> typeAdapter(Gson gson) {
return new AutoValue_Readable.GsonTypeAdapter(gson);
}
}
The use-case for #AutoValue is creating
Generated immutable value classes ...
If you want to change a value, you'd have to create a new instance of the type, updating this one value.
This can be easily implemented with auto-value-with. Just add a with-method to your type.
public abstract Xyz withIsTrue(boolean isTrue);
The extension will implement the method copying all data to the new instance.
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.
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
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.