65.9K
CodeProject is changing. Read more.
Home

Get rid of Parcelable Implemention

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.63/5 (4 votes)

Jan 4, 2015

CPOL
viewsIcon

13329

In this article we will implement a general Parcelable class and name it ParcelableEntity. Then all other entity class can extend it, then they will be parcelable too. So there is no need to implement Parcelable class for other classes.

Introduction

Implementing parcelable class for each entity that we want to pass between activities always was boring for me. So here we will implement it just one time for ever, then our entity classes will extend it and everything is done!

Majesty of reflections will help us.

Background

There are two solution for passing list between activities.

  1. Parcel
  2. Serialize

Here we focus on parcel.

Using the code

First we override writeToParcel method and rewrite it with reflection and looping over each field of entity.

@Override
    public void writeToParcel(Parcel destination, int flags) {

        destination.writeString(this.getClass().getCanonicalName());

        for (Field field : this.getClass().getDeclaredFields()) {
            try {
                field.setAccessible(true);
                destination.writeValue(field.get(this));
            } catch (Exception err) {
                Log.w(TAG, err.toString());
            }

        }

    }

And then we implement Creator interface like below.

public static final Creator CREATOR = new Creator() {
        public ParcelableEntity createFromParcel(Parcel source) {
            try {
                Object entity = Class.forName((source.readString())).newInstance();

                for (Field field : entity.getClass().getDeclaredFields()) {
                    try {
                        field.setAccessible(true);
                       field.set(entity, source.readValue(field.getType().getClassLoader()));

                    } catch (Exception err) {
                        Log.w(TAG, err.toString());
                    }
                }

                return (ParcelableEntity) entity;

            } catch (Exception err) {
                return null;
            }
        }

All done!

We save the child class name in the first line of our writeToParcel method because we need it in our createFromParcel method in order to create correct Object from our child class.

//writeToParcel 
destination.writeString(this.getClass().getCanonicalName());

//createFromParcel 
Object entity = Class.forName((source.readString())).newInstance();

and all in one :

import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;

import java.lang.reflect.Field;
import java.util.ArrayList;

public class ParcelableEntity implements Parcelable {
    private static final String TAG = "ParcelableEntity";

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

    @Override
    public void writeToParcel(Parcel destination, int flags) {

        destination.writeString(this.getClass().getCanonicalName());

        for (Field field : this.getClass().getDeclaredFields()) {
            try {
                field.setAccessible(true);
                if (field.getType().equals(java.util.List.class)) {
                    destination.writeList((ArrayList) field.get(this));
                } else
                    destination.writeValue(field.get(this));
            } catch (Exception err) {
                Log.w(TAG, err.toString());
            }

        }

    }

    public static final Creator CREATOR = new Creator() {
        public ParcelableEntity createFromParcel(Parcel source) {
            try {
                Object entity = Class.forName((source.readString())).newInstance();

                for (Field field : entity.getClass().getDeclaredFields()) {
                    try {
                        field.setAccessible(true);
                        if (field.getType().equals(java.util.List.class)) {
                            ArrayList list = new ArrayList();
                            source.readList(list, Class.forName(field.getDeclaringClass().getName()).getClassLoader());
                            field.set(entity, list);
                        } else
                            field.set(entity, source.readValue(field.getType().getClassLoader()));

                    } catch (Exception err) {
                        Log.w(TAG, err.toString());
                    }
                }

                return (ParcelableEntity) entity;

            } catch (Exception err) {
                return null;
            }
        }

        public ParcelableEntity[] newArray(int size) {
            return new ParcelableEntity[size];
        }
    };

}

and how to use our class :

1- Our entity class will extent above class :

public class Book extends ParcelableEntity {

    public Long id;
    public String name;
}

2- Call destination activity :

public void onClick(View view) {
        ArrayList lstBook = new ArrayList<>();
        Book b1 = new Book();
        b1.id = 1L;
        b1.name = "test 1";
        lstBook.add(b1);
        Book b2 = new Book();
        b2.id = 2L;
        b2.name = "test 2";       
        lstBook.add(b2);

        Intent intent = new Intent(MainActivity.this, SecondActivity.class);
        intent.putParcelableArrayListExtra("TEST", lstBook);
        startActivity(intent);
    }

3- Geting extra in destination activity

protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);

        Bundle extras = getIntent().getExtras();
        List lstBook = extras.getParcelableArrayList("TEST");
    }

Thanks.