readFromParcel() method in Parcelable interface - android

I have a simple doubt in marshaling during service creation. When there is a writeToParcel() method declared in Parcelable interface which is invoked in stub generated (if aidl method parameters are declared as in), why there is no readFromParcel() declaration in Parcelable interface(for out parameters)?
I can create my own readFromParcel() but as per my understanding there should be a overridden readFromParcel() declaration in Parcelable interface if the generated stub wants to invoke it. But the documentation for Parcelable interface does not show any sign of readFromParcel() method. Why is it so? Was it included in previous API version and later got removed? Please explain !
And how different is createFromParcel() from readFromParcel() if both tries to read a parcelable object and populate member fields with the data out of it?

This is because you have declared a parameter of that type as "inout" in your AIDL.
When returned from the method, generated AIDL proxy will call readFromParcel() to update the parameter value (as defined by the "inout" qualifier).

createFromParcel is exactly what it sounds like. A NEW Intance of the parcelable Object/Class that has been written to parcel : Parcelable.writeToParcel() is created. This is a good thing, as it helps prevent memory leaks, as you are not holding on to a reference to the object from another class that may or may not have been destroyed

From the documentation of Parcelable interface :
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();
}
}
When an object of a class which implements the Parcelable interface is to be written to a Parcel, writeToParcel(...) is called.
When an object of this class is to be created using a Parcel, CREATOR.createFromParcel(Parcel in) is called. From there onwards, how the class creates its instance from the Parcel is upto the developer of the class. In the above example, a constructor private MyParcelable(Parcel in) is called from the createFromParcel(...) method.
Conventionally, many developers define a readFromParcel(Parcel in) method in their implementations and call it from the constructor:
private MyParcelable(Parcel in) {
readFromParcel(in);
}
private void readFromParcel(Parcel in) {
mData = in.readInt();
}

Related

Is it possible to create a Parcelable Class that has an empty constructor?

I'm trying to use firestore recycler adapter with a parcelable class, but it needs to have an empty constructor.
My solution now is to create a regular class with an empty constructor and right after fetching the data, I'll map the objects into a parcelable copy.
But is it possible to create a Parcelable Class with an empty constructor? In Android Studio when I do right click -> Generate -> I see no secondary constructor option so I guess it's not possible, right?
Yes, it is possible. The Parcelable object will be serialized and deserialized without any problem.
In Android Studio when I do right click -> Generate -> I see no secondary constructor option so I guess it's not possible, right?
No, the fact that it doesn't appear as suggestion in Android Studio code completion feature doesn't mean it is not possible.
Taking as a reference the Parcelable implementation from Android documentation. You should then need to add an empty constructor. Just write the code, don't use code generator.
public MyParcelable(){
}
The class then should look like this:
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();
}
public MyParcelable(){
}
}

Should Parcel.writeSerializable be used in a Parcelable.writeToParcel?

I am new to android and am having a bit of trouble wrapping my head around the Parcelable interface.
I eventually found this:
https://stackoverflow.com/a/2141166/6647053
The point made in the above answer is that when passing an object to an activity, this:
intent.putExtra("object", parcelableObject);
performs much better than this:
intent.putExtra("object", serializableObject);
My question is:
Would there be any performance benefit to using a Parcel's read / write Serializable methods within the Parcelable (as opposed to just using a serializable object with intent.putExtra)? Why / Why not?
Example:
public class MyParcelable implements Serializable, Parcelable {
/* Some Custom Object Stuff Here */
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeSerializable(this);
}
public static final Parcelable.Creator<MyParcelable> CREATOR = new Parcelable.Creator<MyParcelable>() {
public MyParcelable createFromParcel(Parcel parcel) {
return parcel.readSerializable();
}
public MyParcelable[] newArray(int size) {
return new MyParcelable[size];
}
};
}
There is no benefit to writing this: parceling will be as slow as serializing.
In ordinary Java, Externalizable can perform better than Serializable, because you supply your own readExternal(ObjectInput in) and writeObject(ObjectOutput out) in which you are expected to manually serialize your fields instead of relying on the JVM to introspect and automatically do it for you. Android's Parcelable serves a similar purpose.

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

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.

Implementing Parcelable Class That Requires Context

I'd like for my data class to implement Parcelable so it can be shared between Activities, however it also needs reference to Context so the fields can be saved to SQLiteDatabase.
This however is a problem since Parcelable.Creator method createFromParcel only has one parameter Parcel.
public abstract class Record implements Parcelable {
protected Context context;
protected String value;
public Record(Context context) {
this.context = context;
}
public Record(Parcel parcel) {
this.value = parcel.readString();
}
public void save() {
//save to SQLiteDatabase which requires Context
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel parcel, int flag) {
parcel.writeString(value);
}
public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
public Record createFromParcel(Parcel parcel) {
return new Record(in);
}
public Record[] newArray(int size) {
return new Record[size];
}
};
}
How can a class that implements Parcelable also reference Context so it save to SQLiteDatabase?
The Parcelable interface is like the Java interface Serializable. Objects which implement this interface should be serializable. This means it should be possible to transform the object to a representation which could be saved in a file e.g.
It is easily possible for a string, int, float or double etc, because they all have a string representation. The Context class is clearly not serializable and not parcelable, because it can be an Activity for example.
If you want to save the state of your activity to a database, you should find another way to do that.
Your Record class probably doesn't really need access to the SQL database. The reason for it is exactly the problem you have now: it's very difficult to inject the Context back into each Record.
Perhaps a better solution would be to implement a static RecordSQLService, that has method save(Record r). Your app could start RecordSQLService when the app launches, so it will remain alive as long as your app does, and it takes the responsibility of saving away from the Record class, which makes it so you don't need Context anymore and can Parcel it.

Categories

Resources