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.
- Parcel
- 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.
destination.writeString(this.getClass().getCanonicalName());
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<book> 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);
}
</book>
3- Geting extra in destination activity
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
Bundle extras = getIntent().getExtras();
List<book> lstBook = extras.getParcelableArrayList("TEST");
}
</book>
Thanks.