Can't debug a class that implements parcelable - android

If I have an object called "EditorialCollection" and it implements parcelable why can't I debug into the overrided methods like writeToParcel?
protected EditorialCollection(Parcel in) {
super(in);
//break point here:
items = new ArrayList<>();
in.readList(items, CollectionItem.class.getClassLoader());
embedPagination = (EmbedPagination) in.readValue(EmbedPagination.class.getClassLoader());
}
#Override
public int describeContents() {
return 0;
}
public static final Creator<EditorialCollection> CREATOR = new Creator<EditorialCollection>() {
#Override
public EditorialCollection createFromParcel(Parcel in) {
return new EditorialCollection(in);
}
#Override
public EditorialCollection[] newArray(int size) {
return new EditorialCollection[size];
}
};
I can't hit break points nor can I output logging there. Am I doing something wrong or can someone explain why logging/breakpoints won't work here.
Thanks,
Reid

Parcel objects or Parcelable implementation objects cannot being debugged.
public final class Parcel {
private static final boolean DEBUG_RECYCLE = false;
private static final boolean DEBUG_ARRAY_MAP = false;
private static final String TAG = "Parcel";
.
.
.
}

You can use Parcelables.forceParcel(builder, CREATOR) method for testing Parcel classes.
But it is only available for Test Classes.
Also you can copy this func from Parcelables class. And you can try in java code:
public static <T extends Parcelable> T forceParcel(T parcelable, Creator<T> creator) {
Parcel parcel = Parcel.obtain();
try {
parcelable.writeToParcel(parcel, 0);
parcel.setDataPosition(0);
return creator.createFromParcel(parcel);
} finally {
parcel.recycle();
}
}

Related

Can auto-value-parcel-adapter cope with Typed Set of another auto-value Class?

I am investigating auto-value and its extensions, namely auto-value-parcel and auto-value-parcel-adapter within my Android application.
I have these model classes:-
#AutoValue
public abstract class Xenarchaeota implements Parcelable {
#ParcelAdapter(AmoebaTypeAdapter.class)
public abstract Set<Amoeba> amoebas();
public static Builder builder() {
return new AutoValue_Xenarchaeota.Builder();
}
#AutoValue.Builder
public abstract static class Builder {
public abstract Builder setAmoebas(Set<Amoeba> value);
public abstract Xenarchaeota build();
}
}
and
#AutoValue
public abstract class Amoeba implements Parcelable {
public abstract String surname();
public static Builder builder() {
return new AutoValue_Amoeba.Builder();
}
#AutoValue.Builder
public abstract static class Builder {
public abstract Builder surname(final String value);
public abstract Amoeba build();
}
}
My type adapter is where my issues arise
class AmoebaTypeAdapter implements TypeAdapter<Set<Amoeba>> {
#Override
public Set<Amoeba> fromParcel(Parcel in) {
final ArrayList<Amoeba> arrayList = new ArrayList<>();
in.readTypedList(arrayList, Amoeba.CREATOR); // How to access the CREATOR?
return new TreeSet<>(arrayList);
}
#Override
public void toParcel(Set<Amoeba> value, Parcel dest) {
final ArrayList<Amoeba> arrayList = new ArrayList<>(value);
dest.writeTypedList(arrayList);
}
}
The CREATOR I need to pass to the readTypedArray is declared in AutoValue_Amoeba.
Where is my mistake? misunderstanding of auto-value-parcel?
AutoValue: Parcel extension can't handle sets, but if you convert the property to a List things will work out of the box without a custom adapter. If you want to treat it as a Set you could do this. Keep in mind you'll probably also want to cache the Set.
#AutoValue
public abstract class Xenarchaeota implements Parcelable {
abstract List<Amoeba> amoebaList();
public Set<Amoeba> amoebas() {
return new TreeSet(amoebaList());
}
public static Builder builder() {
return new AutoValue_Xenarchaeota.Builder();
}
#AutoValue.Builder
public abstract static class Builder {
public abstract Builder setAmoebas(Set<Amoeba> value);
public abstract Xenarchaeota build();
}
}
I believe I have identified a solution as follows:-
Currently the auto-value-parcel class
com.ryanharter.auto.value.parcel.AutoValueParcelExtension
has a method called generateCreator:-
FieldSpec generateCreator(ProcessingEnvironment env, TypeName autoValueType, List<Property> properties, TypeName type, Map<TypeMirror, FieldSpec> typeAdapters) {
ClassName creator = ClassName.bestGuess("android.os.Parcelable.Creator");
TypeName creatorOfClass = ParameterizedTypeName.get(creator, type);
...
...
This method generates a Parcelable CREATOR that resembles this
public static final Parcelable.Creator<AutoValue_Amoeba> CREATOR = new Parcelable.Creator<AutoValue_Amoeba>() {
#Override
public AutoValue_Amoeba createFromParcel(Parcel in) {
return new AutoValue_Amoeba(
in.readString()
);
}
#Override
public AutoValue_Amoeba[] newArray(int size) {
return new AutoValue_Amoeba[size];
}
};
If the generateCreator method was changed as follows:-
FieldSpec generateCreator(ProcessingEnvironment env, TypeName autoValueType, List<Property> properties, TypeName type, Map<TypeMirror, FieldSpec> typeAdapters) {
ClassName creator = ClassName.bestGuess("android.os.Parcelable.Creator");
TypeName creatorOfClass = ParameterizedTypeName.get(creator, autoValueType); // CHANGE MADE HERE!!! swap type with autoValueType
...
...
This method would then generate a Parcelable CREATOR that resembles this
public static final Parcelable.Creator<Amoeba> CREATOR = new Parcelable.Creator<Amoeba>() {
#Override
public AutoValue_Amoeba createFromParcel(Parcel in) {
return new AutoValue_Amoeba(
in.readString()
);
}
#Override
public AutoValue_Amoeba[] newArray(int size) {
return new AutoValue_Amoeba[size];
}
};
This CREATOR now allows a TypeAdapter to employ the CREATOR as shown here
class AmoebaTypeAdapter implements TypeAdapter<Set<Amoeba>> {
#Override
public Set<Amoeba> fromParcel(Parcel in) {
final List<Amoeba> arrayList = new ArrayList<>();
in.readTypedList(arrayList, AutoValue_Amoeba.CREATOR);
return new TreeSet<>(arrayList);
}
#Override
public void toParcel(Set<Amoeba> value, Parcel dest) {
final ArrayList<Amoeba> arrayList = new ArrayList<>(value);
dest.writeTypedList(arrayList);
}
}

How to make nested ArrayLists parcelable

To pass an arrayList of objects to a fragment, I have to make the list objects parcelable.
public mObjectClass implements Parcelable {
// Some code
}
The problem is that one of the attributes in my list objects is another object-based arrayList.
public mObjectClass implements Parcelable {
// Some code
private ArrayList<someOtherObject> anotherArrayList;
}
How can I make mObjectClass parcelable?
someOtherObject has to implement Parcelable (not extend has in your question) too. Then you can call parcel.writeTypedList(anotherArrayList); to write it and parcel.readTypedList(yourList, someOtherObject.CREATOR) to read it back. You can read more here
This solution is heavily influenced by some Stackoverflow posts, including
this.
Essentially make both Classes parcelable and make use of someOtherClasses Parcelable.Creator.
mObjectClass:
public class mObjectClass implements Parcelable {
private ArrayList<someOtherObject> anotherArrayList;
//add getter + setter...
public mObjectClass() {
anotherArrayList = new ArrayList<someOtherObject>();
}
public mObjectClass(Parcel in) {
anotherArrayList = new ArrayList<someOtherObject>();
in.readTypedList(anotherArrayList, someOtherObject.CREATOR);
}
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel outParcel, int flags) {
outParcel.writeTypedList(anotherArrayList);
}
public static final Parcelable.Creator<MyParcelable> CREATOR = new Parcelable.Creator<MyParcelable>() {
#Override
public mObjectClass createFromParcel(Parcel in) {
return new mObjectClass(in);
}
#Override
public mObjectClass[] newArray(int size) {
return new mObjectClass[size];
}
};
}
someOtherObject:
public class someOtherObject implements Parcelable {
String someString;
//add getter + setter...
public void writeToParcel(Parcel out, int flags) {
out.writeString(someString);
}
public static final Parcelable.Creator<someOtherObject> CREATOR = new Parcelable.Creator<someOtherObject>() {
public someOtherObject createFromParcel(Parcel in) {
return new someOtherObject(in);
}
public someOtherObject[] newArray(int size) {
return new someOtherObject[size];
}
};
public int describeContents() {
return 0;
}
public someOtherObject(Parcel in) {
someString = in.readString();
}
public someOtherObject() {
}
public someOtherObject(String someString) {
this.someString = someString;
}
}
Tada, you are now able to add mObjectClass as extra to your intents after initialising it and using a setter to set the Arraylist with other someOtherObjects.

How to parcelable class with own objects and enums

I have class tariff and i need to parcelable it.
public class Tariff implements Parcelable{
private String operator;
private Discounts discount;
private boolean unlimited;
private Billings billing;
private String name;
private double price;
private double calculated;
private Call call;
private Sms sms;
I found some advices here but im not sure i´m getting them right.
1) For parcelable enum I found this. Will that parcelable all values of my enum? Or how should i do that?
try {
type = Discounts.valueOf(in.readString());
} catch (IllegalArgumentException x) {
type = null;
}
2) For parcelable another object(for example call) i found this: If the CategoryDate class is one of yours, you can make it Parcelable as well. Then in your class' writeToParcel() call you can call this.date.writeToParcel() and pass it the same Parcel object. This will cause the CategoryDate class to write its data into the same Parcel object which is being used by CloseItPending.
But i´m not sure i got it right. How exactly should i do that?
Parcelable it's really a lot of boilerplate that, coding manually, have a great chance to fall in error.
Use this site: http://www.parcelabler.com/
This tool automatic generate the parcelable fields for your class. Remember to make the Classes declared as class variables to be parcelable too.
The result will be somenthing like this:
public class Tariff implements Parcelable {
private String operator;
private Discounts discount;
private boolean unlimited;
private Billings billing;
private String name;
private double price;
private double calculated;
private Call call;
private Sms sms;
protected Tariff(Parcel in) {
operator = in.readString();
discount = (Discounts) in.readValue(Discounts.class.getClassLoader());
unlimited = in.readByte() != 0x00;
billing = (Billings) in.readValue(Billings.class.getClassLoader());
name = in.readString();
price = in.readDouble();
calculated = in.readDouble();
call = (Call) in.readValue(Call.class.getClassLoader());
sms = (Sms) in.readValue(Sms.class.getClassLoader());
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(operator);
dest.writeValue(discount);
dest.writeByte((byte) (unlimited ? 0x01 : 0x00));
dest.writeValue(billing);
dest.writeString(name);
dest.writeDouble(price);
dest.writeDouble(calculated);
dest.writeValue(call);
dest.writeValue(sms);
}
#SuppressWarnings("unused")
public static final Parcelable.Creator<Tariff> CREATOR = new Parcelable.Creator<Tariff>() {
#Override
public Tariff createFromParcel(Parcel in) {
return new Tariff(in);
}
#Override
public Tariff[] newArray(int size) {
return new Tariff[size];
}
};
}

creating parcelable in android from some of the fields of a class

i have the following class which i intent to pass from one activity to another:
public class Ad extends ListItem implements parcelable{
private String _type;
private String _recordID;
private String _line1;
private String _line2;
private String _line3;
private String _line4;
private String _url;
private IOnUiNeedToUpdateListener _listener;
private boolean _notActive = false;
private String _alertText;
private Double _longitude;
private Double _latitude;
}
i want to pass an array of such objects from one activity to another. however, i do not need to pass all fields.
is it possible to create a parcel only from the desired fields and send it?
It's your code that writes to Parcel and your code that reads from Parcel. So basically yes. You can write whatever you want. Content of all members, content of some, no members, but other values you use to restore state of the object etc, etc.
Try design your class like this..
public class Form implements Parcelable {
private String formdata1;
private String formdata2;
private String formdata3;
private String formdata4;
public Form() {
}
public Form(Parcel in) {
setFormdata1(in.readString());
setFormdata2(in.readString());
setFormdata3(in.readString());
setFormdata4(in.readString());
}
public String getFormdata1() {
return formdata1;
}
public void setFormdata1(String formdata1) {
this.formdata1 = formdata1;
}
public String getFormdata2() {
return formdata2;
}
public void setFormdata2(String formdata2) {
this.formdata2 = formdata2;
}
public String getFormdata3() {
return formdata3;
}
public void setFormdata3(String formdata3) {
this.formdata3 = formdata3;
}
public String getFormdata4() {
return formdata4;
}
public void setFormdata4(String formdata4) {
this.formdata4 = formdata4;
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel in, int arg1) {
in.writeString(getFormdata1());
in.writeString(getFormdata2());
in.writeString(getFormdata3());
in.writeString(getFormdata4());
}
public static final Parcelable.Creator<Form> CREATOR = new Parcelable.Creator<Form>() {
#Override
public Form createFromParcel(Parcel in) {
return new Form(in);
}
#Override
public Form[] newArray(int size) {
return new Form[size];
}
};
}

Read & writing arrays of Parcelable objects

I have following class which reads and writes an array of objects from/to a parcel:
class ClassABC extends Parcelable {
MyClass[] mObjList;
private void readFromParcel(Parcel in) {
mObjList = (MyClass[]) in.readParcelableArray(
com.myApp.MyClass.class.getClassLoader()));
}
public void writeToParcel(Parcel out, int arg1) {
out.writeParcelableArray(mObjList, 0);
}
private ClassABC(Parcel in) {
readFromParcel(in);
}
public int describeContents() {
return 0;
}
public static final Parcelable.Creator<ClassABC> CREATOR =
new Parcelable.Creator<ClassABC>() {
public ClassABC createFromParcel(Parcel in) {
return new ClassABC(in);
}
public ClassABC[] newArray(int size) {
return new ClassABC[size];
}
};
}
In above code I get a ClassCastException when reading readParcelableArray:
ERROR/AndroidRuntime(5880): Caused by: java.lang.ClassCastException: [Landroid.os.Parcelable;
What is wrong in above code? While writing the object array, should I first convert the array to an ArrayList?
UPDATE:
Is it OK to convert an Object array to an ArrayList and add it to parcel? For instance, when writing:
ArrayList<MyClass> tmpArrya = new ArrayList<MyClass>(mObjList.length);
for (int loopIndex=0;loopIndex != mObjList.length;loopIndex++) {
tmpArrya.add(mObjList[loopIndex]);
}
out.writeArray(tmpArrya.toArray());
When reading:
final ArrayList<MyClass> tmpList =
in.readArrayList(com.myApp.MyClass.class.getClassLoader());
mObjList= new MyClass[tmpList.size()];
for (int loopIndex=0;loopIndex != tmpList.size();loopIndex++) {
mObjList[loopIndex] = tmpList.get(loopIndex);
}
But now I get a NullPointerException. Is above approach is correct? Why it is throwing an NPE?
You need to write the array using the Parcel.writeTypedArray() method and read it back with the Parcel.createTypedArray() method, like so:
MyClass[] mObjList;
public void writeToParcel(Parcel out) {
out.writeTypedArray(mObjList, 0);
}
private void readFromParcel(Parcel in) {
mObjList = in.createTypedArray(MyClass.CREATOR);
}
The reason why you shouldn't use the readParcelableArray()/writeParcelableArray() methods is that readParcelableArray() really creates a Parcelable[] as a result. This means you cannot cast the result of the method to MyClass[]. Instead you have to create a MyClass array of the same length as the result and copy every element from the result array to the MyClass array.
Parcelable[] parcelableArray =
parcel.readParcelableArray(MyClass.class.getClassLoader());
MyClass[] resultArray = null;
if (parcelableArray != null) {
resultArray = Arrays.copyOf(parcelableArray, parcelableArray.length, MyClass[].class);
}
ERROR/AndroidRuntime(5880): Caused by: java.lang.ClassCastException: [Landroid.os.Parcelable;
According to the API, readParcelableArray method returns Parcelable array (Parcelable[]), which can not be simply casted to MyClass array (MyClass[]).
But now i get Null Pointer Exception.
It is hard to tell the exact cause without the detailed exception stack trace.
Suppose you have made MyClass implements Parcelable properly, this is how we usually do for serialize/deserialize a array of parcelable objects:
public class ClassABC implements Parcelable {
private List<MyClass> mObjList; // MyClass should implement Parcelable properly
// ==================== Parcelable ====================
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel out, int flags) {
out.writeList(mObjList);
}
private ClassABC(Parcel in) {
mObjList = new ArrayList<MyClass>();
in.readList(mObjList, getClass().getClassLoader());
}
public static final Parcelable.Creator<ClassABC> CREATOR = new Parcelable.Creator<ClassABC>() {
public ClassABC createFromParcel(Parcel in) {
return new ClassABC(in);
}
public ClassABC[] newArray(int size) {
return new ClassABC[size];
}
};
}
Hope this helps.
You can also use the following methods:
public void writeToParcel(Parcel out, int flags) {
out.writeTypedList(mObjList);
}
private ClassABC(Parcel in) {
mObjList = new ArrayList<ClassABC>();
in.readTypedList(mObjList, ClassABC.CREATOR);
}
I had a similar problem, and solved it this way.
I defined a helper method in MyClass, for converting an array of Parcelable to an array of MyClass objects:
public static MyClass[] toMyObjects(Parcelable[] parcelables) {
MyClass[] objects = new MyClass[parcelables.length];
System.arraycopy(parcelables, 0, objects, 0, parcelables.length);
return objects;
}
Whenever I need to read a parcelable array of MyClass objects, e.g. from an intent:
MyClass[] objects = MyClass.toMyObjects(getIntent().getParcelableArrayExtra("objects"));
EDIT: Here is an updated version of the same function, that I am using more recently, to avoid compile warnings:
public static MyClass[] toMyObjects(Parcelable[] parcelables) {
if (parcelables == null)
return null;
return Arrays.copyOf(parcelables, parcelables.length, MyClass[].class);
}
You need to write the array using the Parcel.writeTypedArray() method and read it back with the Parcel.readTypedArray() method, like so:
MyClass[] mObjArray;
public void writeToParcel(Parcel out, int flags) {
out.writeInt(mObjArray.length);
out.writeTypedArray(mObjArray, flags);
}
protected MyClass(Parcel in) {
int size = in.readInt();
mObjArray = new MyClass[size];
in.readTypedArray(mObjArray, MyClass.CREATOR);
}
For lists, you can do the following:
ArrayList<MyClass> mObjList;
public void writeToParcel(Parcel out, int flags) {
out.writeTypedList(mObjList);
}
protected MyClass(Parcel in) {
mObjList = new ArrayList<>(); //non-null reference is required
in.readTypedList(mObjList, MyClass.CREATOR);
}

Categories

Resources