Tuesday, March 22, 2016

Android Parcelable Object


Mission: 
Send one custom object or a list of them from one Activity class to another Activity class.

Before we start, let's take a look at what I have in the project.
  • An Activity Class named FirstActivity.
  • An Activity Class named SecondActivity.
  • An Custom Object Class named City.
public class City {
    private String name;
    private double longitude;
    private double latitude;

    // Constructor, Setter and Getter
}

As you could see, the City class is composed of three properties, name, longitude and latitude with appropriate constructor, setters and getters.


How can we send a list of city objects from FirstActivity to SecondActivity? 

The only tunnel for this task is send the data within an Intent. Intent is like an envelope help you send bunch of data in it to the right place, like an Activity. But the problem is the Intent object could mostly only carry primary type objects, like int, double, String, etc, other than custom object. So, when you need to send a custom object, or list of custom objects along with an Intent object, you need to make this custom object class "Parcelable". It's like pack your data into a parcel, then send it through an Intent object. In the new Activity, when Intent object is received, the parcel will be opened (resolved).


Here is how we could do it:

1, In the City.java file, declare class will implement Parcelable Interface.
import android.os.Parcelable;

public class City implements Parcelable{
    private String name;
    private double longitude;
    private double latitude;

    // Constructor, Setter and Getter
}

Now the Custom Class will be Parcelable, could be carried along by the Intent object.

2, Implement Required Methods for Parcelable Interface.
    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(name);
        dest.writeDouble(longitude);
        dest.writeDouble(latitude);
    }
The first method defines the kind of object you are going to Parcel, you can use the hashCode() here.
The second method is actually trying to pack your object data into the parcel.

PS: If some of the property's type is not primary, you need to "Flat" it to primary type object first. E.g. You have a enum type property, then you need to convert it to int value, then use dest.writeInt(value);

Tip: In Android Studio, when there is an error, there will be a red bulb to indicate the Android Studio has a solution for this. After you add "implements Parcelable" after "class City", the red bulb will show up, click on it and choose implement the Parcelable Interface, it will automatically implements these two methods for you.

3, Classes implementing the Parcelable interface must also have a non-null static field called CREATOR of a type that implements the Parcelable.Creator interface. This is the step where you could unpack data from a parcel, then construct it into a new object.
    protected City(Parcel in) {
        name = in.readString();
        longitude = in.readDouble();
        latitude = in.readDouble();
    }

    public static final Creator CREATOR = new Creator() {
        @Override
        public City createFromParcel(Parcel in) {
            return new City(in);
        }

        @Override
        public City[] newArray(int size) {
            return new City[size];
        }
    };

Tip: In Android Studio, these two methods could also be auto implemented in the way introduced in the last step.

4, How to use it?
  • Send an Intent object with Parcelable object.
  •     City city = new City("New York", 40.7056497, -73.9780035);
        Intent intent = new Intent(getApplicationContext(), SecondActivity.class);
        intent.putExtra("Intent String ID for City Object", city);
        startActivity(intent);
    
  • Send an Intent object with list of Parcelable objects.
  •     City cityNY = new City("New York", 40.7056497, -73.9780035);
        City cityLA = new City("Los Angeles", 34.0204989, -118.4117325);
        ArrayList cityList = new ArrayList<>();
        cityList.add(cityNY);
        cityList.add(cityLA);
        Intent intent = new Intent(getApplicationContext(), SecondActivity.class);
        intent.putParcelableArrayListExtra("Intent String ID for City Object List", cityList);
        startActivity(intent);
    
  • Resolve a Parcelable object from an Intent object.
  •     Intent intent = getIntent();
        City city = intent.getExtras().getParcelable("Intent String ID for City Object");
  • Resolve a list of Parcelable objects from an Intent object.
  •     Intent intent = getIntent();
        ArrayList cityList = intent.getExtras().getParcelableArrayList("Intent String ID for City Object List");
    
  • Array of Parcelable object could also be sent and received along with Intent object.

Full source code of City:

package com.antonio081014.demo.android.parcelabledemo;

import android.os.Parcel;
import android.os.Parcelable;

public class City implements Parcelable {
    private String name;
    private double longitude;
    private double latitude;

    // Constructor, Setter and Getter
    public City(String name, double longitude, double latitude) {
        this.name = name;
        this.longitude = longitude;
        this.latitude = latitude;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getLongitude() {
        return longitude;
    }

    public void setLongitude(double longitude) {
        this.longitude = longitude;
    }

    public double getLatitude() {
        return latitude;
    }

    public void setLatitude(double latitude) {
        this.latitude = latitude;
    }
    
    protected City(Parcel in) {
        name = in.readString();
        longitude = in.readDouble();
        latitude = in.readDouble();
    }

    public static final Creator CREATOR = new Creator() {
        @Override
        public City createFromParcel(Parcel in) {
            return new City(in);
        }

        @Override
        public City[] newArray(int size) {
            return new City[size];
        }
    };

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(name);
        dest.writeDouble(longitude);
        dest.writeDouble(latitude);
    }
}

No comments :

Post a Comment