How to marshall and unmarshall a Parcelable to a byte array with help of Parcel?
First create a helper class ParcelableUtil.java:
public class ParcelableUtil {
public static byte[] marshall(Parcelable parceable) {
Parcel parcel = Parcel.obtain();
parceable.writeToParcel(parcel, 0);
byte[] bytes = parcel.marshall();
parcel.recycle();
return bytes;
}
public static Parcel unmarshall(byte[] bytes) {
Parcel parcel = Parcel.obtain();
parcel.unmarshall(bytes, 0, bytes.length);
parcel.setDataPosition(0); // This is extremely important!
return parcel;
}
public static <T> T unmarshall(byte[] bytes, Parcelable.Creator<T> creator) {
Parcel parcel = unmarshall(bytes);
T result = creator.createFromParcel(parcel);
parcel.recycle();
return result;
}
}
With the help of the util class above, you can marshall/unmarshall instances of your class MyClass implements Parcelable
like so:
Unmarshalling (with CREATOR
)
byte[] bytes = …
MyClass myclass = ParcelableUtil.unmarshall(bytes, MyClass.CREATOR);
Unmarshalling (without CREATOR
)
byte[] bytes = …
Parcel parcel = ParcelableUtil.unmarshall(bytes);
MyClass myclass = new MyClass(parcel); // Or MyClass.CREATOR.createFromParcel(parcel).
Marshalling
MyClass myclass = …
byte[] bytes = ParcelableUtil.marshall(myclass);
Unmarshall Parcelable created by @Parcelize
For now, the best generic solution that I've found is achieved using reflection:
internal inline fun <reified T : Parcelable> ByteArray.deserializeParcelable(): T {
val parcel = Parcel
.obtain()
.apply {
unmarshall(this@deserializeParcelable, 0, size)
setDataPosition(0)
}
return parcelableCreator<T>()
.createFromParcel(parcel)
.also {
parcel.recycle()
}
}
internal inline fun <reified T : Parcelable> parcelableCreator(): Parcelable.Creator<T> {
val creator = T::class.java.getField("CREATOR").get(null)
@Suppress("UNCHECKED_CAST")
return creator as Parcelable.Creator<T>
}
We have to use reflection because the CREATOR
generated by @Parcelize
is not accessible in such methods.
Unmarshalling unknown type code, List Byte & List String
In the super class, this looks wrong:
public Message(Parcel in)
{
...
if (this.imageData!=null) {
in.readByteArray(this.imageData);
}
}
@Override
public void writeToParcel(Parcel dest, int flags) {
...
dest.writeByteArray(imageData);
}
I am relatively certain that this.imageData
will always be null
at that point, which means that you will not have a matching "read" to go with the "write" in writeToParcel()
. Instead, you should use createByteArray()
:
public Message(Parcel in)
{
...
this.imageData = in.createByteArray();
}
Then we have to consider the subclass:
public Emergency(Parcel in)
{
...
in.readList(extrasFormat,Formats.ExtrasFormat.class.getClassLoader());
}
@Override
public void writeToParcel(Parcel dest, int flags) {
...
dest.writeList(extrasFormat);
}
Does Formats.ExtrasFormat
implement Parcelable or Serializable? If it doesn't, I don't believe this will work. If it does, then probably fixing the superclass was all that was needed.
How to use Parcel in Android?
Ah, I finally found the problem. There were two in fact.
- CREATOR must be public, not protected. But more importantly,
- You must call
setDataPosition(0)
after unmarshalling your data.
Here is the revised, working code:
public void testFoo() {
final Foo orig = new Foo("blah blah");
final Parcel p1 = Parcel.obtain();
final Parcel p2 = Parcel.obtain();
final byte[] bytes;
final Foo result;
try {
p1.writeValue(orig);
bytes = p1.marshall();
// Check to make sure that the byte stream seems to contain a Parcelable
assertEquals(4, bytes[0]); // Parcel.VAL_PARCELABLE
p2.unmarshall(bytes, 0, bytes.length);
p2.setDataPosition(0);
result = (Foo) p2.readValue(Foo.class.getClassLoader());
} finally {
p1.recycle();
p2.recycle();
}
assertNotNull(result);
assertEquals( orig.str, result.str );
}
protected static class Foo implements Parcelable {
public static final Parcelable.Creator<Foo> CREATOR = new Parcelable.Creator<Foo>() {
public Foo createFromParcel(Parcel source) {
final Foo f = new Foo();
f.str = (String) source.readValue(Foo.class.getClassLoader());
return f;
}
public Foo[] newArray(int size) {
throw new UnsupportedOperationException();
}
};
public String str;
public Foo() {
}
public Foo( String s ) {
str = s;
}
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel dest, int ignored) {
dest.writeValue(str);
}
}
Using both Parcelable class and Serializable in one class
There are no issues as you don't introduce any new properties for the Parcelable implementation. Standard implementation merely provides you some helper methods describeContents
and writeToParcel
and a static CREATOR
object, which will not be part of the Serialized content.
example:
class MyParcelable private constructor(`in`: Parcel) : Parcelable {
private val mData: Int = `in`.readInt()
override fun describeContents(): Int {
return 0
}
override fun writeToParcel(out: Parcel, flags: Int) {
out.writeInt(mData)
}
companion object {
val CREATOR: Parcelable.Creator<MyParcelable?>
= object : Parcelable.Creator<MyParcelable?> {
override fun createFromParcel(`in`: Parcel): MyParcelable? {
return MyParcelable(`in`)
}
override fun newArray(size: Int): Array<MyParcelable?> {
return arrayOfNulls(size)
}
}
}
}
Parcel through socket
A good example can be found here.
In general, you will need to make sure that you have the classes available to reconstruct (create from parcel) the objects.
Related Topics
No Generated R.Java File in My Project
Get Text from Web Page to String
Error: No Resource Identifier Found for Attribute 'Adsize' in Package 'Com.Google.Example' Main.Xml
Apply a Theme to an Activity in Android
Why Do I Get "Unresolved Reference" Error for My View's Name/Id When I Type It in Kotlin
Creating Temporary Files in Android
Didn't Find Class "Com.Google.Firebase.Provider.Firebaseinitprovider"
Java.Lang.Illegalargumentexception: View Not Attached to Window Manager
Listview with Onitemclicklistener
How to Autoplay HTML5 Mp4 Video on Android
Actionbaractivity Is Deprecated
Transitive Dependencies for Local Aar Library
How to Define a Circle Shape in an Android Xml Drawable File
How to Keep/Exclude a Particular Package Path When Using Proguard
Android Studio Says "Cannot Resolve Symbol" But Project Compiles