First of all, all the code i will refer to is at my repository
I have been having problems parcelizing a PlayList, the parcelizing works wonders but the deserializing ends up on either a NullPointerException or a BadParcelableException. I haven't been able to pinpoint the source of the exceptions, thus i ask you to check my code to see if i'm abusing any OOP principles or outright misusing the API.
You can't pass the all the class as a parameter to parcel.write(). You need to pass just the parameter copy received in the constructor. You also need to ensure that all objects manipulated are or implement ´Parcelable`.
I believe you need to change the follwing:
In constructor
ArrayList<Song> copy;
public PlayList(ArrayList<Song> copy){
super(copy);
this.copy = copy;
}
In write()
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeTypedList(copy);
parcel.writeInt(actualSong);
parcel.writeInt(shuffling ? 1 : 0);
parcel.writeInt(repeating ? 1 : 0);
}
In read()
public PlayList(Parcel serialized) {
super(serialized.readTypedList(copy, Song.WptType.CREATOR));
actualSong = serialized.readInt();
shuffling = serialized.readInt() == 1 ? true : false;
repeating = serialized.readInt() == 1 ? true : false;
}
NOTE
You also need to implement Parcelable in the Song class for this to work, but it looks already done in the code repository.
Related
I have been writing Parcelables to Parcel without any focus on flags field, which is a parameter in the method signature and it has worked fine but I have run into an implementation where I can no longer ignore them:
public static <K extends Parcelable, V extends Parcelable> void write(Parcel dest,
Map<K, V> map, int flags) {
if (map == null) {
dest.writeInt(-1);
} else {
Set<Map.Entry<K, V>> entrySet = map.entrySet();
dest.writeInt(entrySet.size());
for (Map.Entry<K, V> entry : entrySet) {
dest.writeParcelable(entry.getKey(), flags);
dest.writeParcelable(entry.getValue(), flags);
}
}
}
This is a Map to/from Parcelable utility I have written and I am wondering if the flags should be passed as it is to both Key as well as the Value while writing them or should pass 0 for Key and flags for Value.
I read the definition of what a flag is in the docs:
PARCELABLE_WRITE_RETURN_VALUE
added in API level 1
int PARCELABLE_WRITE_RETURN_VALUE
Flag for use with writeToParcel(Parcel, int): the object being
written is a return value, that is the result of a function such as
"Parcelable someFunction()", "void someFunction(out Parcelable)", or "void someFunction(inout Parcelable)". Some
implementations may want to release resources at this point.
Constant Value: 1 (0x00000001)
But am unable to comprehend it. Could anyone explain in simple terms what a Parcelable flag is and how it should be used?
The only currently existing flag (PARCELABLE_WRITE_RETURN_VALUE) is intended for use in AIDL interfaces. It is supposed to hint certain kinds of Parcelable objects, that they are being returned from IPC method, so their associated resources can be released. Fot instance, ContentProvider internally contains AIDL method like this:
ParcelFileDescriptor openFile(String path, int flags);
When you override openFile in a custom ContentProvider, your method returns an open ParcelFileDescriptor… You aren't closing it yourself, and it is not automatically closed during interprocess transfer either (passing descriptors between processes does not imply closing them in Linux). But the descriptor is not leaked! Instead the ParcelFileDescriptor closes itself when written to Parcel:
#Override
public void writeToParcel(Parcel out, int flags) {
if (mWrapped != null) {
try {
mWrapped.writeToParcel(out, flags);
} finally {
releaseResources();
}
} else {
if (mCommFd != null) {
out.writeInt(1);
out.writeFileDescriptor(mFd);
out.writeFileDescriptor(mCommFd);
} else {
out.writeInt(0);
out.writeFileDescriptor(mFd);
}
if ((flags & PARCELABLE_WRITE_RETURN_VALUE) != 0 && !mClosed) {
// Not a real close, so emit no status
closeWithStatus(Status.SILENCE, null);
}
}
}
Since ParcelFileDescriptor is just ordinary class, using facilities of Binder/Parcel to pass FileDescriptor between processes, you can imagine existence of similar classes, that hold onto native resources (memory, file descriptors) and conditionally release them when returned from openFile-like methods.
Likewise, other flags could be used to propagate similar conditional behavior deeply down Parcelable matryoshka. Unfortunately, Android developers haven't defined reasonable rules for introducing such custom flags (unlike e.g. IBinder#FIRST_CALL_TRANSACTION and IBinder#LAST_CALL_TRANSACTION), and AIDL is not widely used in practice outside of Android internals, so I am not aware of any examples of such flags.
You can only provide flag zero or one.
You have a void method, so you're not returning a Parcelable from a function nor do you have a parameter that's Parcelable, as the documentation says, therefore the flag should be zero.
I looked for the Observer and Observable classes (interface) but I need to create something of more generic.
For example if I would build an observable Paint class I should override many methods.. I don't want this.
I thought maybe is possible to create a custom observer that hold all the methods result of the class to observe and after a isChanged() calling return the status (true or false) comparing the older values with the current values.
The following code method can be used to retrieve all methods inside a generic class:
// Found on http://stackoverflow.com/
public static Method[] getAccessibleMethods(Class clazz) {
List<Method> result = new ArrayList<Method>();
while (clazz != null) {
for (Method method : clazz.getDeclaredMethods()) {
int modifiers = method.getModifiers();
if (Modifier.isPublic(modifiers) || Modifier.isProtected(modifiers)) {
result.add(method);
}
}
clazz = clazz.getSuperclass();
}
return result.toArray(new Method[result.size()]);
}
Once we have the methods listed we can call each method and store the results inside an generic array.
When we need to check for some changes we can refresh all the results and comparing with the olds.
So, the question is: is possible to create this kind of custom observer or there is a valid alternative way (or something already done)?
it's obvious that we can only call the methods with determinated characteristic:
- no parameters methods
- no methods that return void
What is the correct way of implementing the Parcelable interface in Android? According to the documentation you should implement the writeToParcel method and have a CREATOR.
public void writeToParcel(Parcel out, int flags) {
out.writeInt(mData);
}
But when I implement it without adding a CREATOR and leaving the writeToParcel() empty the app still seems to work correctly. Sometimes I would get a Bad Parcelable Exception but I can't work out the steps to replicate.
This is how I use to pass an object from activity to fragment
Bundle bundle = new Bundle();
bundle.putParcelable(PageFragment.PAGE_FILTER_KEY, page);
fragment.setArguments(bundle);
So, what is the purpose of adding stuff like out.writeInt(mData); what kind of problems can be expected if this is not done?
Parcelable implementation mainly have two process steps.
1 Writing your java object to Parcel which includes two methods.
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(cityName);
dest.writeString(macroName);
dest.writeString(id);
}
where describe content is for setting a flag for your contents. Most of time you just need to it untouched.
public void writeToParcel(Parcel dest, int flags) , you need to write you Java class object to parcel step by step according to fields in JAVA class. In above example my class has three strings. You can write almost all types of objects in parcel. You just need to chose appropriate one. Like writeString(),writeList() or writeObject() etc.
2. Second part is reading your java object back from parcel
This part required two things as well. First is CREATOR of your java class like following
public static final Creator<City> CREATOR = new Creator<City>() {
#Override
public City createFromParcel(Parcel in) {
return new City(in);
}
#Override
public City[] newArray(int size) {
return new City[size];
}
};
In above example my Java class is City. It makes read a City object from parcel. But it calls new City(in) constructor of City class. So now I need a constructor which accept a parcel object in arguments. Lets create that too..
protected City(Parcel in) {
cityName = in.readString();
macroName = in.readString();
id = in.readString();
}
Now we make a class complete full proof parcelable. One thing to notice, we need to read members in same sequence at protected City(Parcel in) we put them in parcel i.e. in writeToParcel() method.
On how to reproduce badParcelable exeption in simply letting android create java object from parcelable. For that you can choose Destroy activities from developer options on android device and put you app in background in that activity, so android kill your application process ID. Resume your app by recreating activity (onCreate + Bundle), you will get that exception if you does not implemented parcelable correctly.
But when I implement it without adding a CREATOR and leaving the writeToParcel() empty the app still seems to work correctly.
CREATOR is used when reading data back out of a Parcel and converting it back into objects.
writeToParcel() puts your data into the Parcel.
The only way that leaving those off will work correctly is in cases where your Parcelable is not actually being put into a Parcel or reconstituted from a Parcel. Examples include LocalBroadcastManager.
what is the purpose of adding stuff like out.writeInt(mData);
It would be the same purpose as adding stuff like out.write() with an OutputStream: it writes to the output. Your question is akin to asking "hey, if I don't write data to my file, what sorts of problems will I encounter?".
Looking at the docs for Parcel#writeMap(Map), it clearly states that "The Map keys must be String objects", however looking at the code there seems to be absolutely no reason for this aside from completely unnecessary casting:
/* package */ void readMapInternal(Map outVal, int N,
ClassLoader loader) {
while (N > 0) {
Object key = readValue(loader);
Object value = readValue(loader);
outVal.put(key, value);
N--;
}
}
/* package */ void writeMapInternal(Map<String,Object> val) {
if (val == null) {
writeInt(-1);
return;
}
Set<Map.Entry<String,Object>> entries = val.entrySet();
writeInt(entries.size());
for (Map.Entry<String,Object> e : entries) {
writeValue(e.getKey());
writeValue(e.getValue());
}
}
Is there anything I am missing here? It's important for me to know since I am writing a library that heavily uses this class and it seems like a massive restriction for no reason.
EDIT: Similarly, when writing SparseArray objects, this class forces you
to pass in only SparseArray<Object>:
public final void writeSparseArray(SparseArray<Object> val)
This is another restriction that makes no sense. Are these Android API bugs?
EDIT: This answer seems to also imply there is a bug
After a lengthy and unsuccessful search on google, it turns out that there is hardly any information about a handy library called Droid-Fu https://github.com/kaeppler/droid-fu
After reading the introduction by the creator (http://brainflush.wordpress.com/2009/11/16/introducing-droid-fu-for-android-betteractivity-betterservice-and-betterasynctask/) or the API (http://kaeppler.github.com/droid-fu), I could not figure out how to define a new betterasynctask (what methods hold what information etc).
So if there is anyone out there that could provide me (and apperently others as well) with some useful source code or tutorials, I would greatly appreciate it!
(If you need the jar file of the project, let me know, I can send you a copy)
EDIT:
A good example can be found here! and here
Ok, here is some additional information I found in the source code:
A progress dialog is automatically shown. See useCustomDialog(), disableDialog()
If an Exception is thrown from inside doInBackground, this is now handled by the handleError method.
You should now longer override onPreExecute(), doInBackground() and onPostExecute(), instead you should use before(), doCheckedInBackground() and after() respectively.
Let's see what I can achieve from here on then...still looking for a working example though!
EDIT 2:
A couple of examples can be found here and here. I stick to it but I get an error. Only difference is that my AsyncTask is not defined within the activity, but a class of its own. Stepping through the code reveals that the error happens upon creation of the (AsyncTask built-in) Dialog.
This is my stacktrace:
coming in a minute
Droid-fu is a little outdated now, mainly due to the lack of Fragment support. But I'll give an example from an app I wrote that used it.
First, your activity class has to subclass BetterActivity (or BetterXXXActivity). In my code I was using a ListActivity so mine here subclasses BetterListActivity. I also define a subclass of BetterAsyncTask so I can extend some functionality.
public class DroidFuExample extends BetterListActivity {
private ExampleTask mTask;
private List<Stuff> mMainStuff;
private class ExampleTask extends BetterAsyncTask<Void, Void, Integer> {
private List<Stuff> mStuff;
private DroidFuExample mContext; // a context for lifecycle management
...
}
}
Now, my task needs doesn't take a parameter, uses an indeterminate dialog so no progress is published, and needs to return an Integer. Your needs may differ, and that affects the types used in the class definition.
Next step is to define what the task processes in the background. In my case I need to populate mStuff. In your task class, define either doInBackground() or doCheckedInBackground() (doChecked... can throw an exception if you want to catch it).
protected Integer doCheckedInBackground(Context context, Void... params)
throws Exception {
mStuff = // some long-running code (no longer on the UI thread)
return 1;
}
Finally, at the very least you need to do something with your result, like update a class variable or populate the UI or something. This is done in after:
protected void after(Context context, Integer integer) {
if (integer >= someAcceptablePositiveConstant) {
mMainStuff = mStuff;
doSomethingInTheUIWithMainStuff();
} else {
//gah!
}
}
As you mentioned there's more you can do with the class, such as define a before() override that does work on the UI thread before the task, or failed() / handleError() to handle unchecked/checked failures. This is just a simple example, hope it helps.
What a pain in the #ss! The error was well hidden inside the BetterActivityHelper class:
public static ProgressDialog createProgressDialog(final Activity activity,
int progressDialogTitleId, int progressDialogMsgId) {
ProgressDialog progressDialog = new ProgressDialog(activity);
if (progressDialogTitleId <= 0) {
progressDialogTitleId = activity.getResources().getIdentifier(
PROGRESS_DIALOG_TITLE_RESOURCE, "string", activity.getPackageName());
}
progressDialog.setTitle(progressDialogTitleId);
if (progressDialogMsgId <= 0) {
progressDialogMsgId = activity.getResources().getIdentifier(
PROGRESS_DIALOG_MESSAGE_RESOURCE, "string", activity.getPackageName());
}
progressDialog.setMessage(activity.getString(progressDialogMsgId));
progressDialog.setIndeterminate(true);
progressDialog.setOnKeyListener(new OnKeyListener() {
public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
activity.onKeyDown(keyCode, event);
return false;
}
});
progressDialogTitleId and progressDialogMsgId expect a value inside the res/values/string.xml :
<!-- Droid-Fu Progressdialog -->
<string name="droidfu_progress_dialog_title">Some nasty dialog title</string>
<string name="droidfu_progress_dialog_message">Some funny message</string>
If they are not defined, a runtime exception will be thrown.
Unfortunately, even the best helper classes are almost useless if they are UNDOCUMENTED. Took me a couple of hours to figure out what went wrong. So again, NO "Thank you" to the developer. Sor