0

I am trying to implement Parcleable so that I can add it as an extra (this is a requirement).

Here is a slimmed down version of the class SwinImage which only shows relevant details, namely the parts which are derived from Parcleable:

package com.example.kevin.imagemetadata;
import android.os.Parcel;
import android.os.Parcelable;

public class SwinImage implements Parcelable
{
    public SwinImage(String imageName, String location, String[] keywords, String imageDate, boolean share, String email, int rating)
    {
        update(imageName, location, keywords, imageDate, share, email, rating);
    }

    //A constructor for when we havent assigned any metadata.
    public SwinImage(String imageName)
    {
    }

    public void update(String imageName, String location, String[] keywords, String imageDate, boolean share, String email, int rating)
    {
    }

    @Override
    public String toString()
    {
    }

    private void storeImageDetails() {
    }

    @Override
    //We don't need it - but we are forced to due to interface.
    public int describeContents()
    {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel out, int flags)
    {
        out.writeString(imageName);
        out.writeString(location);

        out.writeStringArray(keywords);

        out.writeString(imageDate);

        //Can write boolean array but not boolean...
        boolean[] temp = {share};

        out.writeBooleanArray(temp);
        out.writeString(email);

        out.writeInt(rating);
    }

    public static final Parcelable.Creator<SwinImage> CREATOR = new Parcelable.Creator<SwinImage>()
    {

        @Override
        public SwinImage createFromParcel(Parcel parcel)
        {
            return new SwinImage(parcel);
        }

        @Override
        public SwinImage[] newArray(int i)
        {
            return new SwinImage[i];
        }
    };

    //THE PRIVATE CONSTRUCTOR - FOR INTERNAL USE ONLY.
    private SwinImage(Parcel parcel)
    {
        imageName = parcel.readString();
        location = parcel.readString();

        parcel.readStringArray(keywords);

        imageDate = parcel.readString();

        boolean[] tempArr = new boolean[1];
        parcel.readBooleanArray(tempArr);
        share = tempArr[0];

        email = parcel.readString();
        rating = parcel.readInt();
    }

}

Ok... So, in the calling class, I do this:

public void ClickedImage(View v)
{
    Intent i = new Intent(this, MetaDataActivity.class);

    switch (getResources().getResourceEntryName(v.getId()))
    {
        case "burgerView":
            i.putExtra("SENT_IMAGE", burger); //IT IMPLEMENTS PARCLEABLE THEREFORE WE CAN PASS IT WHERE IT LOOKS FOR A PARCEL
            break;
        case "pitaView":
            i.putExtra("SENT_IMAGE", pita);
            break;
        case "pizzaView":
            i.putExtra("SENT_IMAGE", pizza);
            break;
        case "steakView":
            i.putExtra("SENT_IMAGE", steak);
            break;
    }

    startActivityForResult(i, GET_AND_SET_METADATA_REQUEST);
}

I set a breakpoint at the end, and can indeed see that it is in the intent correctly:

The Intent showing an Extra

However, when it comes to getting it:

@Override
protected void onCreate(Bundle savedInstanceState)
{
    Log.d("Does", "Does this work");

    Intent i;
    i = getIntent();
    image = i.getExtras().getParcelable("SENT_IMAGE");

    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_meta_data);
}

It is never there. The assignment to image crashes the program, and I cannot see through the debugger my SwinImage even existing inside the intent.

What am I doing wrong? Why is it crashing? I have tried for hours solutions (mainly changing how I'm assigning - and some "solutions" have stopped it from crashing, but image is assigned a null)

Can anyone provide some insight? Thanks.

Edit:

I tried moving the getIntent to after onCreate to no avail.

Edit #2: Stack trace:

http://pastebin.com/raw/XJbPxHRv

and types of steak etc:

public class MainActivity extends AppCompatActivity {

    static final int GET_AND_SET_METADATA_REQUEST = 1;  // The request code
    SwinImage burger, pita, pizza, steak;
Kevin
  • 979
  • 4
  • 10
  • 22
  • If you've got a crash, why not to attach crash log from device's logcat? Also show types of variables that your code uses (`image`, `pizza`, etc.). – Sergio Sep 25 '16 at 11:53
  • Hi, Thanks @Serhio I've updated the OP. – Kevin Sep 25 '16 at 12:20

2 Answers2

0

In your SwinImage constructor that takes a Parcel as argument, you do this:

parcel.readStringArray(keywords);

This fails because it attempts to read an array of String into keywords, which is null because you did not initialize it first.

When you call readStringArray(), you pass it a reference to an existing array of String, into which it will copy the array of String from the Parcel.

Note the readStringArray() and writeStringArray() will only work correctly if the array reference that you pass to writeStringArray() is not null and if the number of elements in the array you pass to readStringArray() is exactly the same as the number of elements that you have in the array you pass to writeStringArray().

Instead of readStringArray() you should probably use createStringArray() which doesn't have these restrictions. Use it like this:

keywords = parcel.createStringArray();
David Wasser
  • 93,459
  • 16
  • 209
  • 274
-1

You can't call getIntent() before super.onCreate() - no Intent is available at that point.

Move your super call before the getIntent call.

UPDATE:

After the above fix, your stack trace shows the issue:

NullPointerException: Attempt to get length of null array
at android.os.Parcel.readStringArray(Parcel.java:1026)
at com.example.kevin.imagemetadata.SwinImage.<init>(SwinImage.java:108)

It seems keywords is null, so attempting to read it from the parcel crashes. Either make sure keywords is never null, or put a boolean that says if keywords can be read or not. Also, per this: How to use writeStringArray() and readStringArray() in a Parcel you should probably use createStringArray instead of readStringArray, a lot simpler.

A general note: you should always read your crashes stack traces carefully to better understand why your code isn't working as expected, usually it's the best way to fix broken code.

Community
  • 1
  • 1
marmor
  • 27,641
  • 11
  • 107
  • 150
  • It is not true. `getIntent()` is valid even before `super.onCreate()`. `Activity`'s intent is initialized before dispatching of `onCreate()` callback. Generally it is done via `Activity.attach()`. See [here](https://android.googlesource.com/platform/frameworks/base/+/0e2d281/core/java/android/app/ActivityThread.java#2159) for details. – Sergio Sep 25 '16 at 11:47
  • Hi, I moved it to after the `super.onCreate()` and it still crashes :( – Kevin Sep 25 '16 at 11:49
  • post your stack trace, either intent or getExtras are null. – marmor Sep 25 '16 at 11:52
  • Thanks @marmor I have updated the OP with the stack trace. – Kevin Sep 25 '16 at 12:20
  • Hi! Thank you! It solved my problem, I did upvote but it wont be displayed until I reach 15 reputation :) – Kevin Sep 27 '16 at 12:32