Android Parcelable pass objects between components

In Android, objects references cannot be just passed to/from Activities or Fragments simply. To pass or transfer objects from one activity(or fragment) to another activity Intent/Bundle can be used.
To put the objects in Intent/Bundle first our objects should be either one of the below

  • Serializable
  • Parcelable

Serializable

Serializable is a marker interface, meaning that there is no method to implement, Java will simply do its best effort to serialize it efficiently.

Problem with Serializable in Android

  • The problem with Serializable approach is that reflection is used and it is a slow process. This mechanism also tends to create a lot of temporary objects and cause quite a bit of garbage collection.
  • Serialization is an expensive operation.
  • Performance of the App gets affected.

An alternative solution is to use Parcelable approach.

Parcelable

It is an Interface for classes whose instances can be written to and restored from a Parcel.

A Parcel is a container for a message (data and object references) that can be sent from one component to another component in android.

Advantages of Parcelable over Serializable

  • The Parcelable interface will run the code significantly faster as we are being explicit about the serialization process instead of using reflection to infer it.
  • The code has been heavily optimized which in turn improves app performance.

Parcelable Implementation example

Now we will create a simple app to transfer data from one activity to another activity.

  • In Android Studio, go to File -> New Project and fill all the details required to create a new project.
  • Open the layout file your main activity activity_main.xml and add below code. Here the layout contains two TextViews and a Button.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" 
    android:orientation="vertical"
    tools:context=".MainActivity">
    <android.support.design.widget.TextInputLayout
        android:id="@+id/input_layout_name"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        >
        <EditText
            android:id="@+id/input_name"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:singleLine="true"
            android:hint="Enter Name"
            />
    </android.support.design.widget.TextInputLayout>
    <android.support.design.widget.TextInputLayout
        android:id="@+id/input_layout_country"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        >
        <EditText
            android:id="@+id/input_country"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:singleLine="true"
            android:hint="Enter Country"
            />
    </android.support.design.widget.TextInputLayout>
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/button_send"
        android:text="Send to ReceiverActivity"
        android:onClick="sendToReceiverActivity"/>
</LinearLayout>

Note: Here Material Design TextInputLayout is used. Check the Design support Library TextInputLayout tutorial to know more.

  • First we need to create a UserDetails bean class which implements Parcelable interface. The UserDetails class will carry the name and country details.
public class UserDetails implements Parcelable {
    String name;
    String country;

    public String getName() {
        return name;
    }

    public String getCountry() {
        return country;
    }

    public UserDetails(String name, String country){
        this.name = name;
        this.country = country;
    }
    public UserDetails(Parcel in){
        this.name = in.readString();
        this.country = in.readString();
    }
    @Override
    public void writeToParcel(Parcel dest, int flags){
        dest.writeString(name);
        dest.writeString(country);
    }
    @Override
    public int describeContents(){
        return 0;
    }

    public static final Parcelable.Creator<UserDetails> CREATOR
            = new Parcelable.Creator<UserDetails>(){
        public UserDetails createFromParcel(Parcel in){
            return new UserDetails(in);
        }
        public UserDetails[] newArray(int size){
            return new UserDetails[size];
        }
    };
}

Parcelable Interface Explanation:

  1. First a parameterized constructor with a parameter of type Parcel should be passed which will be used to read the Parcel data in a particular order.
    public UserDetails(Parcel in){
            this.name = in.readString();
            this.country = in.readString();
        }
  2. Second, the writeToParcel method need to be overridden. The writeToParcel method writes the fields to a parcel in a particular order.
     public void writeToParcel(Parcel dest, int flags){
            dest.writeString(name);
            dest.writeString(country);
        }
  3. Third, the describeContents method also need to be overridden and it should return zero. This method is generally not used. Use this link to know more.
    public int describeContents(){
            return 0;
        }
  4. Finally, provide the Parcelable.Creator which is used to create an instance of the class from the Parcel data. The static CREATOR class creates the object from a Parcel via the createFromParcel method that takes in a parcel and passes it to a constructor in the class. The newArray method allows an array of the objects to be parcelled.
     public static final Parcelable.Creator<UserDetails> CREATOR
                = new Parcelable.Creator<UserDetails>(){
            public UserDetails createFromParcel(Parcel in){
                return new UserDetails(in);
            }
            public UserDetails[] newArray(int size){
                return new UserDetails[size];
            }
        };
  • Now open MainActivity.java and make the changes as mentioned below. This activity contains a textbox and button. When the button is clicked then data from textbox will be sent to the ReceiverActivity.java.
public class MainActivity extends AppCompatActivity {
    EditText inputName;
    EditText inputCountry;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        inputName = (EditText)findViewById(R.id.input_name);
        inputCountry =(EditText)findViewById(R.id.input_country);
    }
    public void sendToReceiverActivity(){
        String name = inputName.getText().toString();
        String country = inputCountry.getText().toString();
        UserDetails userDetails = new UserDetails(name, country);
        Bundle b = new Bundle();
        b.putParcelable("user_details", userDetails);
        Intent i = new Intent(getApplicationContext(), ReceiverActivity.class);
        i.putExtras(b);
        startActivity(i);
    }
}

Here,when the Button is clicked then the UserDetails object is created with the input texts. A Bundle is created and the UserDetails object is put in the Bundle and sent to ReceiverActivity through intents.

  • The ReceiverActivity.java class will receive the intent and fetches the User details.
public class ReceiverActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_receiver);
        UserDetails userDetails= getIntent().getExtras().getParcelable("user_details");
        TextView receivedText = (TextView)findViewById(R.id.text_received);
        receivedText.setText("Name: " + userDetails.getName() + " Country: " + userDetails.getCountry());
    }
  • Following is the activity_receiver.xml layout.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
    android:layout_height="match_parent" 
    tools:context="com.techkt.parcelableexample.ReceiverActivity">
<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:id="@+id/text_received"/>
</RelativeLayout>
  • Finally, run and test the app.