import android.os.Parcel;
import android.os.Parcelable;
public class MClass implements Parcelable {
private byte[] _byte;
public MClass() {
}
public MClass(Parcel in) {
readFromParcel(in);
}
public byte[] get_byte() {
return _byte;
}
public void set_byte(byte[] _byte) {
this._byte = _byte;
}
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel dest, int flags) {
dest.writeByteArray(_byte);
}
public void readFromParcel(Parcel in) {
in.readByteArray(_byte); //LOE - Line Of Exception
}
public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
public MClass createFromParcel(Parcel in) {
return new MClass(in);
}
public MClass[] newArray(int size) {
return new MClass[size];
}
};
}
Whenever I am going to retrieve the bytes in my following array it is returning exception of NullPointerException. Can any one say what is the problem? What I am trying to do is to transfer a downloaded image bytes from one activity to another.
You never initialize the _byte array upon reading the parcel, therefore it is null.
What I'd do is, when you write your parcel, store the length of the byte array followed by the actual byte array. When you read the parcel, first read the length and initialize your _byte array to a new array of that size, then read in the byte array.
Code moved from comment
In write...
dest.writeInt(_byte.length);
dest.writeByteArray(_byte);
and in read...
_byte = new byte[in.readInt()];
in.readByteArray(_byte);
A shorter solution without storing the byte arrays length:
dest.writeByteArray(byteArray);
byteArray = in.createByteArray();
Related
If say I have following with a long object. Does below example demonstrate correct way of reading and writing Long object?
Class MyClass implements Parcelable {
private Long aLongObject;
public static final Creator<MyClass> CREATOR = new Creator<MyClass>() {
#Override
public MyClass createFromParcel(Parcel in) {
return new MyClass(in);
}
#Override
public MyClass[] newArray(int size) {
.....
}
};
protected MyClass(Parcel in) {// reading Parcel
super(in);
aLongObject = in.readLong(); // correct way to ready Long object?
}
#Override
public void writeToParcel(#NonNull Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeLong(aLongObject); // is this correct way to send Long?
}
}
The problem with your approach is that a Long value may be null but the Parcel methods only take/ return values of the primitive data type long, see the documentation for details. So you need a workaround to store a Long value.
I like to use a int to indicate whether my Long value is null (you can only store boolean arrays no single boolean values) and a long to store the numerical value if it isn't:
Writing to the Parcel
int indicator = (aLongObject == null) ? 0 : 1;
long number = (aLongObject == null) ? 0 : aLongObject;
dest.writeInt(indicator);
dest.writeLong(number);
Reading from the Parcel
// NOTE: reading must be in the same order as writing
Long aLongObject;
int indicator = in.readInt();
long number = in.readLong();
if(indicator == 0){
aLongObject = null;
}
else{
aLongObject = number;
}
You can simply use
Write
dest.writeValue(this.aLongObject);
Read
this.aLongObject = (Long)in.readValue(Long.class.getClassLoader());
writeValue and readValue handle null gracefully.
Please refer to https://stackoverflow.com/a/10769887/72437
Hi in my app I see an exception in this line of code
intent.hasExtra("meta")
this is my exception
Caused by android.os.BadParcelableException
android.os.Parcel.readParcelableCreator (Parcel.java:2436)
android.os.Parcel.readParcelable (Parcel.java:2358)
android.os.Parcel.readValue (Parcel.java:2264)
android.os.Parcel.readArrayMapInternal (Parcel.java:2614)
android.os.BaseBundle.unparcel (BaseBundle.java:221)
android.os.BaseBundle.containsKey (BaseBundle.java:269)
android.content.Intent.hasExtra (Intent.java:6002)
Unable to start activity ComponentInfo{xxx.Activity}: >android.os.BadParcelableException: Parcelable protocol requires a ?Parcelable.Creator object called CREATOR on class Meta
I cannot understand how could this happen I'm just asking for existing of a key in an intent extras but it throws an exception.
For testing, how can this scenario happen? I create a test app and create a parcelable object and check for meta in intent but my app works correctly and does not throw the exception.
//test case
Intent intent=new Intent();
intent.putExtra("META",new A());
Log.d(TAG, "onCreate:
intent.hasExtra(\"META\")="+intent.hasExtra("META"));
//this log work without exception so why i get exception in my original app?
static class A implements Parcelable{
String d="0dsfa";
int a=10;
public A(){
}
// the order of reading and write parcelable is wrong
// but I want to see that's cause of exception, which my test shows
// that's not my problem
protected A(Parcel in) {
a = in.readInt();
d = in.readString();
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(d);
dest.writeInt(a);
}
private static final Creator<A> CREATOR = new Creator<A>() {
#Override
public A createFromParcel(Parcel in) {
return new A(in);
}
#Override
public A[] newArray(int size) {
return new A[size];
}
};
#Override
public int describeContents() {
return 0;
}
}
I seem to be getting a strange error in my app (see GitHub), which occurs when I pass objects to different activities that implement Parcelable.
I have checked other questions and answers here on Stack Overflow, but I was unable to find a solution. I've tried the answer here, for example - here it is for reference:
-keepclassmembers class * implements android.os.Parcelable {
static ** CREATOR;
}
I've also made sure that the method calls in writeToParcel are in order. Most other questions on Stack Overflow about this issue don't have answers.
Moreover, the reason I am asking a new question is because I think my problem is caused because of how I have used interfaces in my app (I will expand on this point later on). Other questions on Stack Overflow would not suit my particular scenario.
In the following, I have provided links to the code via GitHub, so that you can explore more of the code if required.
When I click on a button to launch a new activity (passing an object that implements Parcelable), there is a crash:
Process: com.satsuware.flashcards, PID: 4664
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.satsuware.flashcards/com.satsumasoftware.flashcards.ui.FlashCardActivity}: java.lang.RuntimeException: Parcel android.os.Parcel#d2219e4: Unmarshalling unknown type code 6815860 at offset 200
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2416)
...
Caused by: java.lang.RuntimeException: Parcel android.os.Parcel#d2219e4: Unmarshalling unknown type code 6815860 at offset 200
at android.os.Parcel.readValue(Parcel.java:2319)
at android.os.Parcel.readListInternal(Parcel.java:2633)
at android.os.Parcel.readArrayList(Parcel.java:1914)
at android.os.Parcel.readValue(Parcel.java:2264)
at android.os.Parcel.readArrayMapInternal(Parcel.java:2592)
at android.os.BaseBundle.unparcel(BaseBundle.java:221)
at android.os.Bundle.getParcelable(Bundle.java:786)
at android.content.Intent.getParcelableExtra(Intent.java:5377)
at com.satsumasoftware.flashcards.ui.FlashCardActivity.onCreate(FlashCardActivity.java:71)
at android.app.Activity.performCreate(Activity.java:6237)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1107)
...
I call the aforementioned activity like so (also see GitHub):
Intent intent = new Intent(TopicDetailActivity.this, FlashCardActivity.class);
intent.putExtra(FlashCardActivity.EXTRA_TOPIC, mTopic);
intent.putExtra(FlashCardActivity.EXTRA_NUM_CARDS, mSelectedNumCards);
intent.putExtra(FlashCardActivity.EXTRA_CARD_LIST, mFilteredCards);
startActivity(intent);
The main part to consider is when I pass mTopic. This is a Topic interface that I created.
However, the Topic interface extends Parcelable and so the objects that implement Topic also include the constructor, CREATOR field, and the methods that a class implementing Parcelable would normally have to have.
You can view the relevant classes via the GitHub links, but I will provide the relevant parts of these classes below. Here is the Topic interface:
public interface Topic extends Parcelable {
int getId();
String getIdentifier();
String getName();
Course getCourse();
ArrayList<FlashCard> getFlashCards(Context context);
class FlashCardsRetriever {
public static ArrayList<FlashCard> filterStandardCards(ArrayList<FlashCard> flashCards, #StandardFlashCard.ContentType int contentType) {
ArrayList<FlashCard> filteredCards = new ArrayList<>();
for (FlashCard flashCard : flashCards) {
boolean isPaper2 = ((StandardFlashCard) flashCard).isPaper2();
boolean condition;
switch (contentType) {
case StandardFlashCard.PAPER_1:
condition = !isPaper2;
break;
case StandardFlashCard.PAPER_2:
condition = isPaper2;
break;
case StandardFlashCard.ALL:
condition = true;
break;
default:
throw new IllegalArgumentException("content type '" + contentType + "' is invalid");
}
if (condition) filteredCards.add(flashCard);
}
return filteredCards;
}
...
}
}
A class (object) that implements Topic:
public class CourseTopic implements Topic {
...
public CourseTopic(int id, String identifier, String name, Course course) {
...
}
#Override
public int getId() {
return mId;
}
#Override
public String getIdentifier() {
return mIdentifier;
}
...
protected CourseTopic(Parcel in) {
mId = in.readInt();
mIdentifier = in.readString();
mName = in.readString();
mCourse = in.readParcelable(Course.class.getClassLoader());
}
public static final Parcelable.Creator<CourseTopic> CREATOR = new Parcelable.Creator<CourseTopic>() {
#Override
public CourseTopic createFromParcel(Parcel in) {
return new CourseTopic(in);
}
#Override
public CourseTopic[] newArray(int size) {
return new CourseTopic[size];
}
};
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mId);
dest.writeString(mIdentifier);
dest.writeString(mName);
dest.writeParcelable(mCourse, flags);
}
}
In one of the last lines of the code above, you can see I pass mCourse, which is a Course object I created. Here it is:
public class Course implements Parcelable {
...
public Course(String subject, String examBoard, #FlashCard.CourseType String courseType,
String revisionGuide) {
...
}
public String getSubjectIdentifier() {
return mSubjectIdentifier;
}
public String getExamBoardIdentifier() {
return mBoardIdentifier;
}
public ArrayList<Topic> getTopics(Context context) {
ArrayList<Topic> topics = new ArrayList<>();
String filename = mSubjectIdentifier + "_" + mBoardIdentifier + "_topics.csv";
CsvParser parser = CsvUtils.getMyParser();
try {
List<String[]> allRows = parser.parseAll(context.getAssets().open(filename));
for (String[] line : allRows) {
int id = Integer.parseInt(line[0]);
topics.add(new CourseTopic(id, line[1], line[2], this));
}
} catch (IOException e) {
e.printStackTrace();
}
return topics;
}
...
protected Course(Parcel in) {
mSubjectIdentifier = in.readString();
mBoardIdentifier = in.readString();
mCourseType = in.readString();
mRevisionGuide = in.readString();
}
public static final Creator<Course> CREATOR = new Creator<Course>() {
#Override
public Course createFromParcel(Parcel in) {
return new Course(in);
}
#Override
public Course[] newArray(int size) {
return new Course[size];
}
};
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(mSubjectIdentifier);
dest.writeString(mBoardIdentifier);
dest.writeString(mCourseType);
dest.writeString(mRevisionGuide);
}
}
I suspect something here may be causing the problem, and is the reason my scenario is different from those in other questions.
To be honest, I'm not exactly sure what may be causing the error, so explanations and guidance in answers would be much appreciated.
Edit:
After David Wasser's suggestions, I have updated parts of my code like so:
FlashCardActivity.java - onCreate(...):
Bundle extras = getIntent().getExtras();
extras.setClassLoader(Topic.class.getClassLoader());
mTopic = extras.getParcelable(EXTRA_TOPIC);
Course.java - writeToParcel(...):
dest.writeString(mSubjectIdentifier);
dest.writeString(mBoardIdentifier);
dest.writeString(mCourseType);
dest.writeInt(mRevisionGuide == null ? 0 : 1);
if (mRevisionGuide != null) dest.writeString(mRevisionGuide);
Course.java - Course(Parcel in):
mSubjectIdentifier = in.readString();
mBoardIdentifier = in.readString();
mCourseType = in.readString();
if (in.readInt() != 0) mRevisionGuide = in.readString();
I've added log messages using Log.d(...) to see if any variables are null when being passed in writeToParcel(...) and used David Wasser's method to properly handle this.
I still get the same error message, however.
Your problem is in LanguagesFlashCard. Here are your parcel/unparcel methods:
protected LanguagesFlashCard(Parcel in) {
mId = in.readInt();
mEnglish = in.readString();
mAnswerPrefix = in.readString();
mAnswer = in.readString();
mTier = in.readInt();
mTopic = in.readParcelable(Topic.class.getClassLoader());
}
As you can see, they don't match. The second item you write to the Parcel is an int, the second item you read from the Parcel is a String.
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mId);
dest.writeInt(mTier);
dest.writeString(mEnglish);
dest.writeString(mAnswerPrefix);
dest.writeString(mAnswer);
dest.writeParcelable(mTopic, flags);
}
Kotlin code for sub data class like ImagesModel also parcelable used
data class MyPostModel(
#SerializedName("post_id") val post_id: String? = "",
#SerializedName("images") val images: ArrayList<ImagesModel>? = null
): Parcelable {
constructor(parcel: Parcel) : this(
parcel.writeString(post_id)
parcel.createTypedArrayList(ImagesModel.CREATOR)
)
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeString(post_id)
parcel.writeTypedList(images)
}
}
Boy, this is a weird one.
I'm sending an intent to a service.
public void updatePortfolio(VehiclePortfolio vehiclePortfolio) {
GenericParcel gp = new GenericParcel(vehiclePortfolio);
Intent apiIntent = new Intent(context, ApiRequestService.class);
apiIntent.putExtra(Constants.ARG_REQUEST, Constants.REQUEST_UPDATE_PORTFOLIO);
apiIntent.putExtra(Constants.ARG_VEHICLE_PORTFOLIO, gp);
// Try getting object back here
//gp = apiIntent.getParcelableExtra(Constants.ARG_VEHICLE_PORTFOLIO);
VehiclePortfolio vp = (VehiclePortfolio)gp.getObject();
String s = apiIntent.getStringExtra(Constants.ARG_REQUEST);
vehiclePortfolio = (VehiclePortfolio) gp.getObject();
// .putExtra(Constants.ARG_VEHICLE_PORTFOLIO, b);
context.startService(apiIntent);
}
When I check the extras in the intent at the end of this function they look fine as shown below.
mMap HashMap (id=830037731672)
[0] HashMap$HashMapEntry (id=830037731800)
key "ARG_VEHICLE_PORTFOLIO" (id=830038543392)
value GenericParcel (id=830038204888)
o VehiclePortfolio (id=830038244704)
configuration VehicleConfiguration (id=830038258216)
id "1375379159508" (id=830038245672)
optionals VehiclePortfolio$_Fields[5] (id=830038244744)
priceReport VehiclePriceReport (id=830038312960)
quotes ArrayList (id=830038417704)
timestamp "2013-08-01T17:46:31.000Z" (id=830038536424)
[1] HashMap$HashMapEntry (id=830037731768)
key "ARG_REQUEST" (id=830037687416)
value Integer (id=830019251512)
value 6
Then, when the service receives the intent, this is what the extras look like. Notice that the vehiclePortfolio has been overwritten on top of ARG_REQUEST extra and the key and value have been swapped. (What?!?)
mMap HashMap (id=830038833832)
[0] HashMap$HashMapEntry (id=830039001656)
key VehiclePortfolio (id=830038934408)
configuration VehicleConfiguration (id=830038936272)
id "1375379159508" (id=830038936128)
optionals null
priceReport VehiclePriceReport (id=830038941176)
quotes ArrayList (id=830038963264)
timestamp "2013-08-01T17:46:31.000Z" (id=830039001424)
value "ARG_REQUEST" (id=830039001576)
[1] HashMap$HashMapEntry (id=830038834568)
key "ARG_VEHICLE_PORTFOLIO" (id=830038833888)
value GenericParcel (id=830038834512)
o Parcel (id=830037350528)
mNativePtr 1891558544
mOwnsNativeParcelObject true
mStack null
Below is the code for GenericParcel which I suspect to be the culprit. If I do a test and pass a string in place of the complex object inside a GenericParcel, behavior is as expected.
public class GenericParcel implements Parcelable {
private Object o;
public GenericParcel(Object in) {
o = in;
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel out, int flags) {
out.writeValue(o);
}
public Object getObject() {
return o;
}
public static final Creator<GenericParcel> CREATOR = new Creator<GenericParcel>() {
public GenericParcel createFromParcel(Parcel source) {
return new GenericParcel(source);
}
public GenericParcel[] newArray(int size) {
return new GenericParcel[size];
}
};
}
I think your implementation of your Parcelable object is not correct. You should have a constructor that takes as its only argument a Parcel (not an Object) and you should be reading values from the Parcel in the same order you wrote them to the Parcel in writeToParcel().
public class GenericParcel implements Parcelable {
private Object o;
public GenericParcel(Parcel in) {
o = in.readValue(null);
}
#Override
public void writeToParcel(Parcel out, int flags) {
out.writeValue(o);
}
// the rest removed for brevity
}
So, I'm having an issue with passing a Parcelable array between two activities. For some reason, the app blows up before it even loads the activity I'm calling.
Here is the object I am trying to pass.
import android.os.Parcel;
import android.os.Parcelable;
public class Inventory implements Parcelable{
public String inventory_id = "" ;
public String vin = "" ;
public String stock = "" ;
public String new_used = "" ;
public String certified = "" ;
public String mileage = "" ;
public Inventory() {}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(inventory_id);
dest.writeString(vin);
dest.writeString(stock);
dest.writeString(new_used);
dest.writeString(certified);
dest.writeString(mileage);
}
public Inventory(Parcel source){
inventory_id = source.readString();
vin = source.readString();
stock = source.readString();
new_used = source.readString();
certified = source.readString();
mileage = source.readString();
}
public static final Parcelable.Creator<Inventory> CREATOR = new Parcelable.Creator<Inventory>() {
public Inventory createFromParcel(Parcel source) {
return new Inventory(source);
}
public Inventory[] newArray(int size) {
return new Inventory[size];
}
};
}
And here is the line where I pass it
Intent intent = new Intent(this, Activity2.class);
intent.putParcelableArrayListExtra("invList", invList); //This is a list of inventory objects.
startActivity(intent);
This code never loads Activity2. I can create a debug point in the onCreate method in Activity 2 and it is never called. If I comment out intent.putParcelableArrayListExtra("invList", invList);, Activity2 will load, but of course blows up when I get to the line that needs the invList, which leads me to believe that something is happening with the Parcelable objects between activities. I get nothing in my LogCat. It just reloads Activity1.
Any help here would be appreciated.