intent.hasExtra("meta") throw bad parcelable exception - android

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

Related

java.lang.RuntimeException: Parcel android.os.Parcel: Unmarshalling unknown type code

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

Get an object result from an activity in Xamarin with Visual Studio

I want to receive an object back from a child activity in Xamarin with Visual Studio 2015:
[Serializable]
class MyObj
{
public string value { get; }
public MyObj(string v)
{
value = v;
}
}
Child Activity
Intent myIntent = new Intent (this, typeof(FirstActivity));
MyObj obj = new MyObj("message");
myIntent.PutExtra ("obj", obj); // cannot convert "obj" to Bundle
SetResult (Result.Ok, myIntent);
Finish();
FirstActivity
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
base.OnActivityResult(requestCode, resultCode, data);
if (requestCode == 0)
if (resultCode == Result.Ok) {
var helloLabel = FindViewById<TextView> (Resource.Id.helloLabel);
MyObj obj = data.GetSerializableExtra("obj") as MyObj;
helloLabel.Text = obj.Text.ToString();
}
}
}
This code causes an error cannot convert obj to Bundle. I've also tried to implement Java.IO.ISerializable is MyObj but I couldn't get a right implementation. It always throws System.NotSupportedException: Unable to activate instance of type MyApp.MyObj from native handle 0x10001d (key_handle 0x1a027cb)
class Object1 : Java.Lang.Object, Java.IO.ISerializable
{
public string value { get; }
public Object1(string v)
{
value = v;
}
}
I would like to get some advice. I'm new in Xamarin and I'm working with Visual Studio 2015
I'd always go for Parcelable. It's very fast! (10x faster) http://www.developerphil.com/parcelable-vs-serializable/
Here is how you implement it in Xamarin:
public class MyObj : Java.Lang.Object, IParcelable
{
public string Value { get; set; }
public MyObj()
{
}
private MyObj(Parcel parcel)
{
// read your values in order
Value = parcel.ReadString();
}
public void WriteToParcel(Parcel dest, ParcelableWriteFlags flags)
{
// read your values in order
dest.WriteString(Value);
}
// -- stuff below here is needed from the parcel interfaces/mechanism --
[ExportField("CREATOR")]
public static MyObjCreator InitializeCreator()
{
return new MyObjCreator();
}
public class MyObjCreator : Java.Lang.Object, IParcelableCreator
{
public Java.Lang.Object CreateFromParcel(Parcel source)
{
return new MyObj(source);
}
public Java.Lang.Object[] NewArray(int size)
{
return new MyObj[size];
}
}
public int DescribeContents()
{
return 0;
}
}
SetResult
Intent myIntent = new Intent(this, typeof(FirstActivity));
MyObj obj = new MyObj {Value = "Hello"};
myIntent.PutExtra("obj", obj);
SetResult(Result.Ok, myIntent);
Finish();
OnActivityResult
var x = (MyObj)data.GetParcelableExtra("obj");
Passing complex objects is a bit tricky. You can use libraries like Json.Net to serialize to string before sending, and then deserialize on the other end.
myIntent.PutExtra ("obj", JsonConvert.SerializeObject(obj));
//in your receiving activity OnActivityResult...
var objectAsString = intent.GetStringExtra("obj")
var result = JsonConvert.DeserializeObject<MyObject>(objectAsString)
Nice and easy solution and well performing too..

Intent extras seem to get corrupted

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
}

Broadcast not received when intent contains parcelable extra

I have an IntentService that is making a network call and receiving back some JSON data. I package this response data in custom object that implements parcelable. If I add this parcelable object to an intent as an extra and then launch an activity using that intent, everything seems to work as expected, i.e. I can retrieve the parcelable from the intent in the newly created activity. However, if I create the intent from within the onHandleIntent() method of my IntentService and then use sendBroadcast(), the broadcast receiver's onReceive() method never fires. If I don't add the parcelable to the intent, though, the onReceive() method fires as expected. Following are some relevant code snippets:
Parcelable Object:
public class JsonResponse implements Parcelable {
private int responseCode;
private String responseMessage;
private String errorMessage;
public JsonResponse() {
}
/*
/ Property Methods
*/
public void setResponseCode(int code) {
this.responseCode = code;
}
public void setResponseMessage(String msg) {
this.responseMessage = msg;
}
public void setErrorMessage(String msg) {
this.errorMessage = msg;
}
/*
/ Parcelable Methods
*/
public static final Creator<JsonResponse> CREATOR = new Creator<JsonResponse>() {
#Override
public JsonResponse createFromParcel(Parcel parcel) {
return new JsonResponse(parcel);
}
#Override
public JsonResponse[] newArray(int i) {
return new JsonResponse[i];
}
};
private JsonResponse(Parcel parcel) {
responseCode = parcel.readInt();
responseMessage = parcel.readString();
errorMessage = parcel.readString();
}
#Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeInt(responseCode);
parcel.writeString(responseMessage);
parcel.writeString(errorMessage);
}
#Override
public int describeContents() {
return 0;
}
}
onHandle() of IntentService:
protected void onHandleIntent(Intent intent) {
service = new LoginService();
service.login("whoever", "whatever");
JsonResponse response = new JsonResponse();
response.setResponseCode(service.responseCode);
response.setResponseMessage(service.responseMessage);
response.setErrorMessage(service.errorMessage);
Intent i = new Intent();
i.putExtra("jsonResponse", response);
i.setAction(ResultsReceiver.ACTION);
i.addCategory(Intent.CATEGORY_DEFAULT);
sendBroadcast(i);
}
Any ideas? Any insight would be greatly appreciated.
It appears that the problem has to do with the size of the object being added as an extra. When one of the string properties of the response object grows too large, the broadcast apparently fails. I have no sources to confirm this, only some trial and error in manipulating one of the strings while leaving all other variables of the equation constant.

Transferring ByteArray through Parcel returns NullPointerException

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

Categories

Resources