Parcel vs. Serializeable for exchanging objects among services/activities - android

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?

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.

Is it possible to a List of Objects in putExtra

I know putExtra can be used to pass objects/strings around between actives. But I am trying you put an ArrayList of objects like ArrayList<Foo> in putExtra
Is this possible?
No it isn't. You'll need to serialize your object into some kind of string representation. One possible string representation is JSON, and one of the easiest ways to serialize to/from JSON in android, if you ask me, is through Google GSON.
Also if you're just passing objects around then Parcelable was designed for this. It requires a little more effort to use than using Java's native serialization, but it's way faster (and I mean way, WAY faster).
From Docs :
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();
}
}
You can use
intent.putParcelableArrayListExtra() for passing Arraylist in intent.
Refer to http://developer.android.com/reference/android/content/Intent.html#putParcelableArrayListExtra(java.lang.String, java.util.ArrayList)
EDIT :
one more link : Help with passing ArrayList and parcelable Activity
Only for very limited and particular types of "Foo". If i recall correctly Double and Long (or maybe it was Integer?) being those types. There might be a way to smuggle a more generic ArrayList through by encapsulating it in some Serializable Object, but I'm not sure about that.

Is it possible to send a custom object with an Intent as an Extra

I'm asking this question: instread of giving a string, a int and so on, can we push a custom object during the creation fo a new Intent?
newActivity.PutExtra("JsonDataResult", business.getJSON());
In fact I have one object constructed thanks to a JSON (from webrequest) , I parse it and I put it on an object.
At this point I'm passing the string returned from the webrequest to another intent but the parsing takes a long time tu be done, so it could be super-cool the ability to pass custom object with intent.
EDIT : I'm using monodroid / xamarin, so
Android.OS.IParcelable cannot be implemented,
Java.IO.ISerializable cannot be implemented.
You can either let your custom classes implement Parcelable (Google says its faster, but you have to do more coding) or Serializable.
Then add your objects to a bundle (or to the "extra"):
Bundle b = new Bundle()
b.putParcelable("myObject",myObject);
b.putSerializable("myObject",myObject);
For info to Parcelablecheckout this
And if you're interested in the difference between Parcelable and Serializable in more detail check out this
I personally prefer the usage of Serializable for simple object-passing, since the code ist not spoiled with so much code.
Edit: ok isn't your question very similar to this then?
As you've specified you're using Monodroid, it looks like it's not straightforward. I did a quick search and found this forum post
Which listed the following solutions to this problem in Monodroid:
Store the custom Object to be passed as a global variable somewhere, and just read it from your second activity
Which is a bit messy and bad practice, but would work.
Or
serialize your class to a string and send the string to the second Activity
Which will be a little more hard work, but better practice
This is an example how to create a Parcelable class:
public class Person implements Parcelable {
private String name;
private String surname;
private String email;
// Get and Set methods
#Override
public int describeContents() {
return hashCode();
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeString(surname);
dest.writeString(email);
}
// We reconstruct the object reading from the Parcel data
public Person(Parcel p) {
name = p.readString();
surname = p.readString();
email = p.readString();
}
public Person() {}
// We need to add a Creator
public static final Parcelable.Creator<person> CREATOR = new Parcelable.Creator<person>() {
#Override
public Person createFromParcel(Parcel parcel) {
return new Person(parcel);
}
#Override
public Person[] newArray(int size) {
return new Person[size];
}
};
Give a look here if you want to use Parcelable.

Class not found when unmarshalling when passing Parcelable through Messenger to remote service

I have a Parcelable object which I use to pass it from Activity to remote service. When I pass it using AIDL interface, everything sounds fine.
Recently, I try to pass it through Messenger from Activity.
// TEST TEST TEST!
StockInfo stockInfo0 = new StockInfo(Code.newInstance("code0"), Symbol.newInstance("symbol0"));
StockInfo stockInfo1 = new StockInfo(Code.newInstance("code1"), Symbol.newInstance("symbol1"));
StockInfo stockInfo2 = new StockInfo(Code.newInstance("code2"), Symbol.newInstance("symbol2"));
List<StockInfo> stockInfos = new ArrayList<StockInfo>();
stockInfos.add(stockInfo0);
stockInfos.add(stockInfo1);
stockInfos.add(stockInfo2);
StockInfosEx stockInfosEx = new StockInfosEx(stockInfos, "abc");
msg.obj = stockInfosEx;
try {
mService.send(msg);
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
I'm getting the following exception in remote service.
02-21 22:55:16.546: E/Parcel(8365): Class not found when
unmarshalling: com.example.testonmessenger.StockInfosEx, e:
java.lang.ClassNotFoundException:
com.example.testonmessenger.StockInfosEx
I was wondering, what can get wrong in between? Here is my Parcelable object.
public class StockInfosEx implements Parcelable {
public final List<StockInfo> stockInfos;
public final String searchedString;
public StockInfosEx(List<StockInfo> stockInfos, String searchedString) {
this.stockInfos = stockInfos;
this.searchedString = searchedString;
}
////////////////////////////////////////////////////////////////////////////
// Handling Parcelable nicely.
public static final Parcelable.Creator<StockInfosEx> CREATOR = new Parcelable.Creator<StockInfosEx>() {
public StockInfosEx createFromParcel(Parcel in) {
return new StockInfosEx(in);
}
public StockInfosEx[] newArray(int size) {
return new StockInfosEx[size];
}
};
private StockInfosEx(Parcel in) {
stockInfos = new ArrayList<StockInfo>();
in.readTypedList(stockInfos, StockInfo.CREATOR);
searchedString = in.readString();
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeTypedList(stockInfos);
parcel.writeString(searchedString);
}
// Handling Parcelable nicely.
////////////////////////////////////////////////////////////////////////////
}
To get complete source code, kindly download from https://www.dropbox.com/s/n69yuhddpb8vedz/testonmessenger.zip
Not Workable Approach (Because our Parcelable is custom, not part of Framework like Rect)
Activity
msg.obj = stockInfosEx;
Remote Service
StockInfosEx stockInfosEx = (StockInfosEx)msg.obj;
Workable Approach
Activity
msg.getData().putParcelable("data", stockInfosEx);
Remote Service
msg.getData().setClassLoader(StockInfosEx.class.getClassLoader());
StockInfosEx stockInfosEx = (StockInfosEx)msg.getData().getParcelable("data");
Now, after I read back the documentation of msg.obj (http://developer.android.com/reference/android/os/Message.html#obj) again, only I understand what it really mean by Parcelable of a framework class
An arbitrary object to send to the recipient. When using Messenger to
send the message across processes this can only be non-null if it
contains a Parcelable of a framework class (not one implemented by the
application). For other data transfer use setData(Bundle).
Note that Parcelable objects here are not supported prior to the FROYO
release.
You're probably not using the right ClassLoader. You need to keep track of the ClassLoader that is marshalling the class in the first place, and use THAT ClassLoader to unmarshall it.
When unmarshalling, you're using current thread's ClassLoader, which is not your UIThread but Android system thread, and as such, has no info about your custom classes.
I used a static class that contained my ClassLoader to solve this (similar approaches can be used without having it to be static).
Something like:
ClassLoaderHelper.setClassLoader(Thread.currentThread().getContextClassLoader());
Then when unmarshalling:
public final void readFromParcel(final Parcel in) {
id = in.readString();
appInfo = in.readParcelable(ClassLoaderHelper.getClassLoader());
...
}
See this other question for more detailed information (probably a duplicate btw).
I'm getting the following exception in remote service.
If you are truly getting this from the remote service, it is because the remote service app does not contain that class. If you are going to use custom Parcelable classes, both the client and the server must have the same class definition.
If, however, your stack trace feels like your Parcelable is being accessed from a core OS process, then you cannot pass Parcelable objects via obj reliably. I have only ever used obj on Message for in-process object passing, never for cross-process messages.

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

Categories

Resources