Android: putExtra parcelable stops activity launching. No error - java

I'm attempting to make an app, and at this point in the app I'm launching an activity, and adding some parcelable extras to the activity. However, with the addition of my extras, the intent will not start the activity. Commenting the putExtra line allows it to function. There is no error to speak of, and the application simply loops and allows the use of the previous activity again.
The problem code lies here:
//puzOut is a Puzzle object (code below)
intent intent = new Intent(ChooseActivity.this, GameActivity.class);
intent.putExtra("puzzle", puzOut); // <- prevents activity starting
startActivity(intent);
The data in putExtra is an object from my app, Puzzle. Below is the code for that, including the Parcelling code.
public class Puzzle implements Parcelable{
private String Name;
private String pictureSet;
private Fruit_Tile[] fruitArray;
int rows;
int columns;
public Puzzle(String puzzleId, String picSet, Fruit_Tile[] array, int rowCount, int columnCount)
{
Name = puzzleId;
pictureSet = picSet;
fruitArray = array;
rows = rowCount;
columns = columnCount;
}
private Puzzle(Parcel in) {
Name = in.readString();
pictureSet = in.readString();
Parcelable[] parcelableArray = in.readParcelableArray(Fruit_Tile.class.getClassLoader());
//Fruit_Tile[] results = null;
if(parcelableArray != null)
{
fruitArray = Arrays.copyOf(parcelableArray, parcelableArray.length, Fruit_Tile[].class);
}
rows = in.readInt();
columns = in.readInt();
}
public String getName()
{
return Name;
}
public String getPictureSet()
{
return pictureSet;
}
public Fruit_Tile[] getFruitArray()
{
return fruitArray;
}
public int getRows()
{
return rows;
}
public int getColumns()
{
return columns;
}
#Override
public void writeToParcel(Parcel out, int flags){
out.writeString(Name);
out.writeString(pictureSet);
out.writeParcelableArray(fruitArray, flags);
out.writeInt(rows);
out.writeInt(columns);
}
public static final Parcelable.Creator<Puzzle> CREATOR = new Parcelable.Creator<Puzzle>(){
public Puzzle createFromParcel(Parcel in) {
return new Puzzle(in);
}
public Puzzle[] newArray(int size) {
return new Puzzle[size];
}
};
#Override
public int describeContents()
{
return 0;
}
}
The puzzle object uses another object, Fruit_Tile, which I have also tried to make parcellable. Below is the code for that.
public class Fruit_Tile implements Parcelable
{
private Bitmap fruit;
private int x_Position;
private int y_Position;
public Fruit_Tile(Bitmap fruitInput, int x, int y)
{
fruit = fruitInput;
x_Position = x;
y_Position = y;
}
private Fruit_Tile(Parcel in)
{
fruit = Bitmap.CREATOR.createFromParcel(in);
x_Position = in.readInt();
y_Position = in.readInt();
}
public Bitmap getImage()
{
return fruit;
}
public void setImage(Bitmap input)
{
fruit = input;
}
public int getX_Position()
{
return x_Position;
}
public int getY_Position()
{
return y_Position;
}
public void setX_Position(int x) { x_Position = x; }
public void setY_Position(int y) { y_Position = y; }
public int describeContents()
{
return 0;
}
#Override
public void writeToParcel(Parcel out, int flags)
{
fruit.writeToParcel(out, flags);
out.writeInt(x_Position);
out.writeInt(y_Position);
}
public static final Parcelable.Creator<Fruit_Tile> CREATOR = new Parcelable.Creator<Fruit_Tile>(){
public Fruit_Tile createFromParcel(Parcel in){
return new Fruit_Tile(in);
}
public Fruit_Tile[] newArray(int size){
return new Fruit_Tile[size];
}
};
}
When I debug and step, following the line
startActivity(intent);
if I step continually, the program eventually returns to the Looper.java code and loops. I can then access the UI again of the first activity, and start the whole process again.
Why is my activity not starting with this putExtra line? There are no errors to speak of (that I have been able to find).
To reiterate, if the putExtra is commented out, the activity will launch.
I'd really appreciate some help!

It happens when you try to write null value in the parcel. In the following code snippet if the Name String is null it will cause activity launching issue.
out.writeString(Name);
So the solution is, either set default value of the class data members at the time of declaration. like
String Name = "";
Or at the time of writing to parcel add a check if its null then don't write it in parcel. Like
if (Name != null) {
out.writeString(Name);
}

Related

Parcelable object changes completely when sent as an Extra of an Intent

I'm trying to put a Parcelable object as an extra in an intent and pass it to the next Activity, and it doesn't crash but the object changes dramatically. I'm sending when clicking on an item from a RecyclerView in a Fragment and opening an Activity from it.
This is how I send it:
AdminProfile adminProfile = list.get(position).admin;
Intent intent = new Intent(view.getContext(),ClosedChatActivity.class);
intent.putExtra("chat",adminProfile);
view.getContext().startActivity(intent);
This how I get it:
adminProfile = (AdminProfile) getIntent().getExtras().getParcelable("chat");
And here the class:
public class AdminProfile implements Parcelable {
public static final Creator<AdminProfile> CREATOR = new Creator<AdminProfile>() {
#Override
public AdminProfile createFromParcel(Parcel in) {
return new AdminProfile(in);
}
#Override
public AdminProfile[] newArray(int size) {
return new AdminProfile[size];
}
};
public Long idUser;
public String name;
public String professio;
public String description;
public List<WebLink> webLinks;
public Long idOficina;
protected AdminProfile(Parcel in) {
if (in.readByte() == 0) {
idUser = null;
} else {
idUser = in.readLong();
}
name = in.readString();
professio = in.readString();
description = in.readString();
webLinks = in.createTypedArrayList(WebLink.CREATOR);
if (in.readByte() == 0) {
idOficina = null;
} else {
idOficina = in.readLong();
}
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeLong(idUser);
parcel.writeString(name);
parcel.writeString(professio);
parcel.writeString(description);
parcel.writeLong(idOficina);
parcel.writeList(webLinks);
}
}
I can't understand why, but when I send the object I have UserId=3, but when I get it it's userId=55834574848. Any ideas?
The Parcelable functions were filled automatically by Android Studio, and reading the first byte messed it up.
Changing
if (in.readByte() == 0) {
idUser = null;
} else {
idUser = in.readLong();
}
for
idUser = in.readLong();
fixed it.

How do I iterate through an ArrayList of custom objects from Intent and add them into LinearLayout?

I have an ArrayList of custom FlightData objects within the intent. I load the intent and get the arraylist as null, and the foreach loop also forces me to use Object as type.
Saving arraylist into intent:
intent.putParcelableArrayListExtra("FlightDataList", (ArrayList<? extends Parcelable>) flightDataList);
Loading of intent:
Intent intent = getIntent();
LinearLayout layout_datasheet = findViewById(R.id.layout_datasheet);
List flightDataList = intent.getParcelableArrayListExtra("FlightDataList");
if (flightDataList == null){
Log.d("flightDataList_size", "FlightDataList is null"); // this fires
}
assert flightDataList != null;
for (Object data : flightDataList){
data = (FlightData) data; // items in list are of type FlightData
TextView tv = new TextView(this);
tv.setText(data.toString());
layout_datasheet.addView(tv);
}
My custom class' parcelable functions (x,y,time, has getters-setters):
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeDouble(x);
dest.writeDouble(y);
dest.writeDouble(time);
}
public static final Creator<FlightData> CREATOR = new Creator<FlightData>() {
#Override
public FlightData createFromParcel(Parcel in) {
return new FlightData(in);
}
#Override
public FlightData[] newArray(int size) {
return new FlightData[size];
}
};
1.First Implement Parceable in your FlightData object model / pojo / class
2.val flightDataList= ArrayList<FlightData>()
3.val args = Bundle()
4.args.putParcelableArrayList("FlightDataList", flightDataList)
5.intent.putExtra(args)
Then to get list
val flightDataList = context.getIntent().getExtras().getParcelableArrayList("FlightDataList")
I doubt that you have implemented Parcable in FlightData
https://medium.com/techmacademy/how-to-implement-and-use-a-parcelable-class-in-android-part-1-28cca73fc2d1
It should work. The only thing that I am missing in your example is the constructor. It could explain the null your are getting.
Try adding this constructor for FlightData
public FlightData(Parcel in) {
x = in.readDouble();
y = in.readDouble();
time = in.readDouble();
}
did you try creating a datastructure that implements parcelable?
public class flightDataList implements Parcelable{
String dataThingyString;
int dataThingyInt;
public flightDataList(String dataThingyString, int dataThingyInt){
this.dataThingyString = dataThingyString;
this.dataThingyInt = dataThingyInt;
}
public flightDataList(Parcle in){
this.dataThingyString = in.readString();
this.dataThingyInt = in.readInt();
}
#Override
public void writeToParcel(Parcel dest, int flags){
dest.writeString(dataThingyString);
dest.writeInt(dataThingyInt);
}
public static final Creator<flightDataList> CREATOR = new Creator<flightDataList>(){
#Override
public flightDataList createFromParcel(Parcel source){
return new flightDataList(source);
}
#Override
public flightDataList[] newArray(int size){
return new flightDataList[size];
}
}
public void setdataThingyString(String stringData){
this.dataThingyString = stringData;
}
public void setdataThingyInt(int intData){
this.dataThingyInt = intData;
}
public String getdataThingyString(){
return dataThingyString;
}
public int getdataThingyInt(){
return dataThingyInt;
}
#Override
public int describeContents(){
return 0;
}
}

Parcelable list inside Parcelable class not reading back with Parcel.readTypedList

I have two Activities, A and B. I am trying to send object from Activity A, to Activity B. When in Activity A, I can see that my List contains two items, but when I retrieve it in Activity B, the List contains 7000000+ records.
Here is my Assessment class, that implements Parcelable, and contains an ArrayList<Photo> which should be parcelable as well.
Assessment POJO:
public class Assessment extends BaseObservable implements Parcelable {
public Assessment(){
}
#SerializedName("Vehicle")
private String vehicle;
#SerializedName("Photos")
private List<Photo> photos;
#Bindable
public String getVehicle() {
return vehicle;
}
public void setVehicle(String vehicle) {
this.vehicle = vehicle;
notifyPropertyChanged(BR.vehicle);
}
public List<Photo> getPhotos() {
return photos;
}
public void setPhotos(List<Photo> photos) {
this.photos = photos;
}
protected Assessment(Parcel in) {
vehicle = in.readString();
photos = new ArrayList<Photo>();
in.readTypedList(photos, Photo.CREATOR);
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(vehicle);
dest.writeTypedList(photos);
}
#SuppressWarnings("unused")
public static final Parcelable.Creator<Assessment> CREATOR = new Parcelable.Creator<Assessment>() {
#Override
public Assessment createFromParcel(Parcel in) {
return new Assessment(in);
}
#Override
public Assessment[] newArray(int size) {
return new Assessment[size];
}
};
}
Photo POJO:
public class Photo implements Parcelable {
public Photo(){
}
#SerializedName("PhotoPath")
private String photoPath;
public String getPhotoPath() {
return photoPath;
}
public void setPhotoPath(String photoPath) {
this.photoPath = photoPath;
}
#SerializedName("Base64PhotoString")
private String photoBase64String;
public String getPhotoBase64String() {
return photoBase64String;
}
public void setPhotoBase64String(String photoBase64String) {
this.photoBase64String = photoBase64String;
}
protected Photo(Parcel in) {
photoPath = in.readString();
photoBase64String = in.readString();
}
//region parelable
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(photoPath);
dest.writeString(photoBase64String);
}
#SuppressWarnings("unused")
public static final Parcelable.Creator<Photo> CREATOR = new Parcelable.Creator<Photo>() {
#Override
public Photo createFromParcel(Parcel in) {
return new Photo(in);
}
#Override
public Photo[] newArray(int size) {
return new Photo[size];
}
};
//endregion
}
Here is how I send the object via Intent from Activity A, to Activity B:
public void OnAdapterItemClicked(View view){
Intent activityIntent = new Intent(this, com.example.andrewkp.gaassessing.DisplayAssessment.class);
Assessment extraAssessment = getAssessmentFromCollection(view); //extraAssessment.getPhotos().size() == 2
activityIntent.putExtra("assessment", extraAssessment);
startActivity(activityIntent);
}
And here is how I read the Parcelable object in Activity B:
assessment = getIntent().getExtras().getParcelable("assessment");
I have looked at the following article, and I follow exactly what they do, but my photos list does not persist through to Activity B:
When I debug the readTypedList method in Parcel class, I can see that it adds 7000000+ records to my ArrayList, but never removes them. Why is this behavior happening?
You are able to put up to 1MB of data in a Bundle encapsulated inside Intent.
You will get bunch of errors when sending PhotoBase64String in Bundle.
However, in order to overcome this issue, I would suggest path/URI of your photo to your second activity. Then in your second activity, read photo from that path, and perform your desired operation.

How to pass object with List of other object between activities using Parcelable?

I have an object called Order which I want to pass between activities. Currently I am using Parcelable to do so.
public class Order implements Parcelable {
private String email;
private Long timestamp;
private List<OrderItem> items;
public Order() { }
private Order(Parcel in) {
email = in.readString();
timestamp = in.readLong();
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(email);
if (timestamp == null) {
dest.writeByte((byte) 0);
} else {
dest.writeByte((byte) 1);
dest.writeLong(timestamp);
}
dest.writeTypedList(items);
}
#Override
public int describeContents() {
return 0;
}
public static final Creator<Order> CREATOR = new Creator<Order>() {
#Override
public Order createFromParcel(Parcel in) {
return new Order(in);
}
#Override
public Order[] newArray(int size) {
return new Order[size];
}
};
// Getters
...
}
The items field is a List of OrderItem objects which implement the Parcelable interface.
public class OrderItem implements Parcelable {
private String orderedClothingId;
private int quantity;
public OrderItem() { }
public OrderItem(String orderedClothingId, int quantity) {
this.orderedClothingId = orderedClothingId;
this.quantity = quantity;
}
private OrderItem(Parcel in) {
orderedClothingId = in.readString();
quantity = in.readInt();
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(orderedClothingId);
dest.writeInt(quantity);
}
#Override
public int describeContents() {
return 0;
}
public static final Creator<OrderItem> CREATOR = new Creator<OrderItem>() {
#Override
public OrderItem createFromParcel(Parcel in) {
return new OrderItem(in);
}
#Override
public OrderItem[] newArray(int size) {
return new OrderItem[size];
}
};
}
Now to pass an Order object called order from one activity to another I do the following:
Intent intent = new Intent(mContext, ActivityTwo.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.putExtra(ORDER_DETAIL_INTENT_EXTRA_KEY, order);
mContext.startActivity(intent);
In ActivityTwo I collect the Order object like so:
Bundle data = getIntent().getExtras();
assert data != null;
mOrder = data.getParcelable(ORDER_DETAIL_INTENT_EXTRA_KEY);
However, when I log the items field contained in the Order object in ActivityTwo it is null. How do I pass the original non-null Order object between activities without the items list being null?
First you miss to read the array back with dest = in.readTypedList(emptyList, CREATOR);
But second and more important, you need to write/read the same ammount of arguments, since you have a if in your writeToParcel you need the same when reading:
private Order(Parcel in) {
email = in.readString();
if(in.readByte() == 1)
timestamp = in.readLong(); //here to skip just like the writeToParcel
in.readTypedList(items = new ArrayList<OrderItem>(), OrderItem.CREATOR);
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(email);
if (timestamp == null) {
dest.writeByte((byte) 0);
} else {
dest.writeByte((byte) 1);
dest.writeLong(timestamp);
}
dest.writeTypedList(items);
}
From first glance it looks like you are passing different different keys within your parcelable
ORDER_DETAIL_INTENT_EXTRA_KEY in the first and CLOTHING_ADMIN_DETAIL_INTENT_EXTRA_KEY in the 2nd. They should both be the same, so pick which one.
Also you can use getIntent().getParcelableExtra() instead of having to use a Bundle

NullPointerException when I try to read a Vector passed through Intent

I'm trying to use Parcelable classes to give a Stack from an activity to another. In order to do this I defined MyStack and MyVector3 in the following way. This is then included in a Model class.
Model class
public class Model implements Parcelable{
public List<MyStack> surfaces;
public Info info;
public Model (){}
public Model(List<MyStack> surf){
surfaces = surf;
}
public void setInfo(Info i){
info=i;
}
public Info getInfo(){
return info;
}
public List<MyStack> getSurfaces(){
return this.surfaces;
}
public int numSurfaces(){
return surfaces.size();
}
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel out, int flags) {
out.writeTypedList(surfaces);
//Parcelable infoP = ((Parcelable) info);
//out.writeParcelable(infoP, 0);
}
public static final Parcelable.Creator<Model> CREATOR
= new Parcelable.Creator<Model>() {
public Model createFromParcel(Parcel in) {
return new Model(in);
}
public Model[] newArray(int size) {
return new Model[size];
}
};
private Model(Parcel in) {
surfaces = new ArrayList<MyStack>();
in.readTypedList(surfaces, MyStack.CREATOR);
//info = in.readParcelable(Info.class.getClassLoader());
}
public class Info{
String title;
String descr;
public Info(String t, String d){
title=t;
descr=d;
}
public Info(String t){
title=t;
}
public void setDescr(String d){
descr=d;
}
}
}
MyStack class
public class MyStack implements Parcelable {
public Stack<MyVector3> stack;
public MyStack(MyStack ms){
this.stack=ms.stack;
}
public MyStack(Stack s){
stack= s;
}
public Stack getStack(){
return this.stack;
}
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel out, int flags) {
out.writeTypedList(stack);
}
public static final Parcelable.Creator<MyStack> CREATOR
= new Parcelable.Creator<MyStack>() {
public MyStack createFromParcel(Parcel in) {
return new MyStack(in);
}
public MyStack[] newArray(int size) {
return new MyStack[size];
}
};
private MyStack(Parcel in) {
stack= new Stack<MyVector3>();
in.readTypedList(stack, MyVector3.CREATOR);
}
}
MyVector3 class
public class MyVector3 extends Vector3 implements Parcelable {
public Vector3 vector;
public MyVector3(Vector3 v){
vector=v;
}
public MyVector3(double x, double y, double z){
vector= new Vector3(x,y,z);
}
public Vector3 getVector(){
return this.vector;
}
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel out, int flags) {
out.writeDoubleArray(new double[]{
this.x,
this.y,
this.z});
}
public static final Parcelable.Creator<MyVector3> CREATOR
= new Parcelable.Creator<MyVector3>() {
public MyVector3 createFromParcel(Parcel in) {
return new MyVector3(in);
}
public MyVector3[] newArray(int size) {
return new MyVector3[size];
}
};
private MyVector3(Parcel in) {
double[] data = new double[3];
in.readDoubleArray(data);
this.x= data[0];
this.y= data[1];
this.z= data[2];
}
}
This is the intent creation, where the model is well populated and I get all values correctly from Logs
Model model= new Model(surfaces);
Intent intent = new Intent(this, EditorPresenter.class);
intent.putExtra("model", model);
startActivity(intent);
And where I use it
Intent intent = getIntent();
model= intent.getParcelableExtra("model");
MyStack[] sf = model.surfaces.toArray(new MyStack[model.numSurfaces()]);
//surf = new Stack();
surf = new Stack[model.numSurfaces()];
Log.i(TAG, "ss="+Integer.toString(sf.length)); //expected value
for(int s=0; s<sf.length; s++) {
Log.i(TAG, "s="+Integer.toString(s)); //expected value
Stack st= new Stack();
Log.i(TAG, "vv="+Integer.toString(sf[s].getStack().size())); //expected value
for(int v=0; v<sf[s].getStack().size(); v++) {
Log.i(TAG, "v="+Integer.toString(v) +sf[s].stack.elementAt(v).getVector().toString()); //NullPointerException
MyVector3 mv = (MyVector3) sf[s].stack.elementAt(v);
if(mv!=null) st.add(mv.getVector());
}
surf[s]=st;
}
But I get a NullPointerException anytime I try to read/write a Vector3 value.
java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String org.rajawali3d.math.vector.Vector3.toString()' on a null object reference
The problem is probably in Parcelable classes, til I get a wrong model back from the intent (number of faces and vertices expected are right, vector values are probably null). Any help is really appreciated.
you are not instantiating your Vector object, after reading from parcelable. Change
private MyVector3(Parcel in) {
double[] data = new double[3];
in.readDoubleArray(data);
this.x= data[0];
this.y= data[1];
this.z= data[2];
}
with
private MyVector3(Parcel in) {
double[] data = new double[3];
in.readDoubleArray(data);
this.x= data[0];
this.y= data[1];
this.z= data[2];
vector = new Vector3(x,y,z);
}
You can generate Parcelable classes from your code using this tool http://www.parcelabler.com/ and try again with new generated classes.

Categories