in my application i'm trying to use Parcelable class to save savedInstanceState bundle, for this action i'm create this below code:
public class SaveInstanceState implements Parcelable {
String stringStates;
Integer integerStates;
boolean booleanStates;
public SaveInstanceState(String str) {
this.stringStates = str;
}
public SaveInstanceState(Integer value) {
this.integerStates = value;
}
public SaveInstanceState(boolean value) {
this.booleanStates = value;
}
public SaveInstanceState(String str, Integer ivalue, boolean bvalue) {
this.stringStates = str;
this.integerStates = ivalue;
this.booleanStates = bvalue;
}
private SaveInstanceState(Parcel in) {
stringStates = in.readString();
integerStates = in.readInt();
booleanStates = in.readByte() != 0;
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel out, int flags) {
out.writeString(stringStates);
out.writeInt(integerStates);
out.writeByte((byte) (booleanStates ? 1 : 0));
}
public static final Parcelable.Creator<SaveInstanceState> CREATOR = new Parcelable.Creator<SaveInstanceState>() {
#Override
public SaveInstanceState createFromParcel(Parcel in) {
return new SaveInstanceState(in);
}
#Override
public SaveInstanceState[] newArray(int size) {
return new SaveInstanceState[size];
}
};
}
in Activity i'm try to save into class by:
private ArrayList<SaveInstanceState> InstanceStateList;
public class ActivityMainView ... {
if (savedInstanceState == null || !savedInstanceState.containsKey("ActivityMain")) {
InstanceStateList = new ArrayList<SaveInstanceState>();
InstanceStateList.add(new SaveInstanceState(mLastMenuItemSelected));
InstanceStateList.add(new SaveInstanceState(mIsShowingExitDialogs));
} else
InstanceStateList = savedInstanceState.getParcelableArrayList("ActivityMain");
}
now for this blow function i can not get from Parcable class and restore savedInstanceState from it
public void RestoreInstanceState(Bundle savedInstanceState) {
savedInstanceState.getParcelableArrayList("ActivityMain");
}
whats my code problem?
Related
I have a Car class implements Parcelable:
public class Car implements Parcelable {
private long id;
private String model;
public Car(long id, String model) {
this.id = id;
this.model = model;
}
public String getModel() {
return model;
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeLong(this.id);
dest.writeString(this.model);
}
private Car(Parcel in) {
this.id = in.readLong();
this.model = in.readString();
}
public static final Creator<Car> CREATOR = new Creator<Car>() {
#Override
public Car createFromParcel(Parcel source) {
return new Car(source);
}
#Override
public Car[] newArray(int size) {
return new Car[size];
}
};
}
Then, I have a Store class which holds a list of cars, it also implements Parcelable:
public class Store implements Parcelable {
private List<Car> carList = new ArrayList<>();
public List<Car> getCarList() {
return carList;
}
public void setCarList(List<Car> cars) {
carList = cars;
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeTypedList(this.carList);
}
private Store(Parcel in) {
in.readTypedList(carList, Car.CREATOR);
}
public static final Creator<Store> CREATOR = new Creator<Store>() {
#Override
public Store createFromParcel(Parcel source) {
return new Store(source);
}
#Override
public Store[] newArray(int size) {
return new Store[size];
}
};
}
In a Fragment, I try to restore the Store instance named myStore, including the list of cars in it:
class MyFragment extends Fragment {
private Store myStore;
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null && savedInstanceState.containsKey(MY_STORE)) {
myStore = savedInstanceState.getParcelable(MY_STORE);
// PROBLEM IS HERE:
// when runtime hit here, the restored myStore contains one car but has id with weird long value and null in model field. WHY?
} else {
// initialize myStore with car list (only one car)
Car myCar = new Car(1, "BMW X1");
List<Car> cars = new ArrayList<>();
cars.add(myCar);
myStore = new Store();
myStore.setCarList(cars);
}
}
#Override
public void onSaveInstanceState(#NonNull Bundle outState) {
if(myStore != null) {
outState.putParcelable(MY_STORE, myStore);
}
super.onSaveInstanceState(outState);
}
}
Problem occur when in MyFragment, code tries to restore myStore in onCreate(). At runtime , the restored myStore does contain one car in a list, but the car in that list has id with weird long value (e.g. 28429333427126393) and null in model field. WHY? What did I do wrong? How to restore an object contains a list of another object?
This problem usually occurs when your carList instance was null so you wrote a null object in parcelable and try to read from it.
One of the options is that you initialize your list when it's null and then write it in parcelable. Using this case your list will always have a reference after restoration which might not be what you're looking for.
The other approach is to write a byte value before writing your list into parcelable indicating that your list was null or you should expect some value from it. For this case I'm using this helper class which solved my own problem. Based on my needs, I have made some improvements. Here is my version if you'd prefer to use it:
public class ParcelableUtils {
public static void write(Parcel dest, String string) {
dest.writeByte((byte) (string == null ? 0 : 1));
if (string != null) {
dest.writeString(string);
}
}
public static String readString(Parcel source) {
if (source.readByte() == 1) {
return source.readString();
}
return null;
}
public static void write(Parcel dest, Parcelable parcelable, int flags) {
dest.writeByte((byte) (parcelable == null ? 0 : 1));
if (parcelable != null) {
dest.writeParcelable(parcelable, flags);
}
}
public static <T extends Parcelable> T readParcelable(Parcel source) {
if (source.readByte() == 1) {
return source.readParcelable(null);
}
return null;
}
public static <T extends Parcelable> T readParcelable(Parcel source, Class objectClass) {
if (source.readByte() == 1) {
return source.readParcelable(objectClass.getClassLoader());
}
return null;
}
public static void write(Parcel dest, Map<String, String> strings) {
if (strings == null) {
dest.writeInt(-1);
}
{
dest.writeInt(strings.keySet().size());
for (String key : strings.keySet()) {
dest.writeString(key);
dest.writeString(strings.get(key));
}
}
}
public static Map<String, String> readStringMap(Parcel source) {
int numKeys = source.readInt();
if (numKeys == -1) {
return null;
}
HashMap<String, String> map = new HashMap<String, String>();
for (int i = 0; i < numKeys; i++) {
String key = source.readString();
String value = source.readString();
map.put(key, value);
}
return map;
}
public static <T extends Parcelable> void write(Parcel dest, Map<String, T> objects, int flags) {
if (objects == null) {
dest.writeInt(-1);
} else {
dest.writeInt(objects.keySet().size());
for (String key : objects.keySet()) {
dest.writeString(key);
dest.writeParcelable(objects.get(key), flags);
}
}
}
public static <T extends Parcelable> Map<String, T> readParcelableMap(Parcel source) {
int numKeys = source.readInt();
if (numKeys == -1) {
return null;
}
HashMap<String, T> map = new HashMap<String, T>();
for (int i = 0; i < numKeys; i++) {
String key = source.readString();
T value = source.readParcelable(null);
map.put(key, value);
}
return map;
}
public static void write(Parcel dest, URL url) {
dest.writeString(url.toExternalForm());
}
public static URL readURL(Parcel source) {
try {
return new URL(source.readString());
} catch (MalformedURLException e) {
e.printStackTrace();
}
return null;
}
public static void write(Parcel dest, Date date) {
dest.writeByte((byte) (date == null ? 0 : 1));
if (date != null) {
dest.writeLong(date.getTime());
}
}
public static Date readDate(Parcel source) {
if (source.readByte() == 1) {
return new Date(source.readLong());
}
return null;
}
public static <T extends java.lang.Enum<T>> void write(Parcel dest, java.lang.Enum<T> enu) {
if (enu == null) {
dest.writeString("");
} else {
dest.writeString(enu.name());
}
}
public static <T extends java.lang.Enum<T>> T readEnum(Parcel dest, Class<T> clazz) {
String name = dest.readString();
if ("".equals(name)) {
return null;
}
return java.lang.Enum.valueOf(clazz, name);
}
public static void write(Parcel dest, boolean bool) {
dest.writeByte((byte) (bool ? 1 : 0));
}
public static boolean readBoolean(Parcel source) {
return source.readByte() == 1;
}
public static <T extends Parcelable> void write(Parcel dest,
List<T> fields, int flags) {
if (fields == null) {
dest.writeInt(-1);
} else {
dest.writeInt(fields.size());
for (T field : fields) {
dest.writeParcelable(field, flags);
}
}
}
#SuppressWarnings("unchecked")
public static <T extends Parcelable> List<T> readParcelableList(
Parcel source) {
int size = source.readInt();
if (size == -1) {
return null;
}
ArrayList<T> list = new ArrayList<T>();
for (int i = 0; i < size; i++) {
list.add((T) source.readParcelable(null));
}
return list;
}
}
Hope it helps.
I want to pass my object oManagement from activity X to Y.
In X:
oManagement = new OrdersManagement();
myIntent = new Intent(this, Y.class);
Then I fill my oManagement object with separate thread.
Then I am waiting when oManagement filled and start another activity:
while (oManagement.allOrders.size() == 0) {
try {
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (oManagement.allOrders.size() > 0) {
myIntent.putExtra("OrdersManagementObject", oManagement);
this.startActivity(myIntent);
}
}
And in my Y Activity:
Bundle b = this.getIntent().getExtras();
if (b != null) {
oManagement = b.getParcelable("OrdersManagementObject");
}
And now surprise -> oManagement is not null, but oManagement.allOrders.size() is 0! :( So i pass my object, but ArrayList inside by object is 0 (in moment of passing was 1 element inside that Array).
How to fix that?
EDIT:
OrdersManagement class:
public class OrdersManagement implements Parcelable {
public ArrayList<QuantorsOrder> allOrders = new ArrayList<QuantorsOrder>();
public OrdersManagement() {
}
public OrdersManagement(Parcel in) {
}
public static final Creator<OrdersManagement> CREATOR = new Creator<OrdersManagement>() {
#Override
public OrdersManagement createFromParcel(Parcel in) {
return new OrdersManagement(in);
}
#Override
public OrdersManagement[] newArray(int size) {
return new OrdersManagement[size];
}
};
public void updateListOfOrders(JSONArray jsonArray) throws JSONException {
if (jsonArray != null && jsonArray.length() > 0)
{
JSONObject json;
for (int i = 0; i < jsonArray.length(); i++)
{
json = (JSONObject)jsonArray.get(i);
if(!IsOrderAlreadyExists(json))
addOrder(json);
}
}
}
private boolean IsOrderAlreadyExists(JSONObject json) throws JSONException
{
if (allOrders.isEmpty())
return false;
else
{
for(QuantorsOrder order : allOrders)
{
if (String.valueOf(order.getId()).equals(json.get("id").toString()))
{
return true;
}
}
return false;
}
}
private void addOrder(JSONObject json) throws JSONException {
allOrders.add(new QuantorsOrder(json));
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
}
}
Your QuantorsOrdershould also implement Parcelable.
And in writeToParcel() method you should write the list and read it in constructor like this:
public void writeToParcel(Parcel out, int flags) {
out.writeTypedList(allOrders);
}
private OrdersManagement (Parcel in) {
in.readTypedList(allOrders, QuantorsOrder.CREATOR);
}
You should also implement the logic of writing and reading field in your QuantorsOrder class.
Here is example on simple class:
public class MyParcelable implements Parcelable {
private int mData;
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel out, int flags) {
out.writeInt(mData);
}
public static final Parcelable.Creator<MyParcelable> CREATOR
= new Parcelable.Creator<MyParcelable>() {
public MyParcelable createFromParcel(Parcel in) {
return new MyParcelable(in);
}
public MyParcelable[] newArray(int size) {
return new MyParcelable[size];
}
};
public MyParcelable(Parcel in) {
mData = in.readInt();
}
}
The main thing here is happening in writeToParcel(...) method and in constructor public MyParcelable(Parcel in).
I generate parcelable implementation with plugin and that is the code i got.
I have a an error compilation on Metrics(Parcel in).
It probably because the generic array. How can i fix it?
The generic T can can contain String, Number or boolean only.
public class Metrics<T extends Parcelable> implements Parcelable {
private T[] yData;
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeTypedArray(this.yData, flags);
}
public Metrics() {
}
protected Metrics(Parcel in) {
this.yData = in.createTypedArray(T.CREATOR);
}
public static final Parcelable.Creator<Metrics> CREATOR = new Parcelable.Creator<Metrics>() {
#Override
public Metrics createFromParcel(Parcel source) {
return new Metrics(source);
}
#Override
public Metrics[] newArray(int size) {
return new Metrics[size];
}
};
}
You can use a List instead of an array :
public class Metrics<T extends Parcelable> implements Parcelable {
private List<T> yData;
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
if (yData == null || yData.size() == 0)
dest.writeInt(0);
else {
dest.writeInt(yData.size());
final Class<?> objectsType = yData.get(0).getClass();
dest.writeSerializable(objectsType);
dest.writeList(yData);
}
}
public Metrics() {
}
protected Metrics(Parcel in) {
int size = in.readInt();
if (size == 0) {
yData = null;
} else {
Class<?> type = (Class<?>) in.readSerializable();
yData = new ArrayList<>(size);
in.readList(yData, type.getClassLoader());
}
}
public static final Parcelable.Creator<Metrics> CREATOR = new Parcelable.Creator<Metrics>() {
#Override
public Metrics createFromParcel(Parcel source) {
return new Metrics(source);
}
#Override
public Metrics[] newArray(int size) {
return new Metrics[size];
}
};
}
I have a class Tag Model and i need to send List< TagModel> to another activity.
If i implement parcelable then I have another custom object array list in its items.
How to solve this problem?
public class TagModel {
int listing_count;
ArrayList<ListingsModel> listingsModel; //how to handle this list
String foodTypeName;
public int getListing_count() {
return listing_count;
}
public void setListing_count(int listing_count) {
this.listing_count = listing_count;
}
public String getFoodTypeName() {
return foodTypeName;
}
public void setFoodTypeName(String foodTypeName) {
this.foodTypeName = foodTypeName;
}
public ArrayList<ListingsModel> getListings() {
return listingsModel;
}
public void setListings(ArrayList<ListingsModel> listingsModel) {
this.listingsModel = listingsModel;
}
}
You 2 objects have to be Parceable to send.
I'm assuming your object "ListingsModel" is already parceable.
TagModel Parceable
public class TagModel implements Parcelable {
int listing_count;
ArrayList<ListingsModel> listingsModel; //how to handle this list
String foodTypeName;
public int getListing_count() {
return listing_count;
}
public void setListing_count(int listing_count) {
this.listing_count = listing_count;
}
public String getFoodTypeName() {
return foodTypeName;
}
public void setFoodTypeName(String foodTypeName) {
this.foodTypeName = foodTypeName;
}
public ArrayList<ListingsModel> getListings() {
return listingsModel;
}
public void setListings(ArrayList<ListingsModel> listingsModel) {
this.listingsModel = listingsModel;
}
protected TagModel(Parcel in) {
listing_count = in.readInt();
if (in.readByte() == 0x01) {
listingsModel = new ArrayList<ListingsModel>();
in.readList(listingsModel, ListingsModel.class.getClassLoader());
} else {
listingsModel = null;
}
foodTypeName = in.readString();
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(listing_count);
if (listingsModel == null) {
dest.writeByte((byte) (0x00));
} else {
dest.writeByte((byte) (0x01));
dest.writeList(listingsModel);
}
dest.writeString(foodTypeName);
}
#SuppressWarnings("unused")
public static final Parcelable.Creator<TagModel> CREATOR = new Parcelable.Creator<TagModel>() {
#Override
public TagModel createFromParcel(Parcel in) {
return new TagModel(in);
}
#Override
public TagModel[] newArray(int size) {
return new TagModel[size];
}
};
}
For parcelable object I recommend using this great tool parcelabler
I have a Activity A which has a ArrayList of object which i parceable..
Object is :
public class DashBoardPOJO implements Parcelable {
private String dashBoardName;
private String dashBoardId;
private String dashBoardStatus;
private ArrayList<DashBoardDataPOJO> dashBoardDataList;
private boolean loaded;
public ArrayList<DashBoardDataPOJO> getDashBoardDataList() {
return dashBoardDataList;
}
public void setDashBoardDataList(
ArrayList<DashBoardDataPOJO> dashBoardDataList) {
this.dashBoardDataList = dashBoardDataList;
}
public boolean isLoaded() {
return loaded;
}
public void setLoaded(boolean loaded) {
this.loaded = loaded;
}
public String getDashBoardStatus() {
return dashBoardStatus;
}
public void setDashBoardStatus(String dashBoardStatus) {
this.dashBoardStatus = dashBoardStatus;
}
#Override
public int describeContents() {
// TODO Auto-generated method stub
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(dashBoardName);
dest.writeString(dashBoardId);
dest.writeList(dashBoardDataList);
}
public static final Parcelable.Creator<DashBoardPOJO> CREATOR = new Parcelable.Creator<DashBoardPOJO>() {
public DashBoardPOJO createFromParcel(Parcel in) {
return new DashBoardPOJO(in);
}
public DashBoardPOJO[] newArray(int size) {
return new DashBoardPOJO[size];
}
};
public DashBoardPOJO() {
dashBoardName = null;
dashBoardId = null;
dashBoardDataList = null;
/*
* userName=null; userId=null;
*/
}
private DashBoardPOJO(Parcel in) {
dashBoardName = in.readString();
dashBoardId = in.readString();
in.readList(dashBoardDataList, null);
}
// GETTERS AND SETTERS............
public String getDashBoardName() {
return dashBoardName;
}
public void setDashBoardName(String dashBoardName) {
this.dashBoardName = dashBoardName;
}
public String getDashBoardId() {
return dashBoardId;
}
public void setDashBoardId(String dashBoardId) {
this.dashBoardId = dashBoardId;
}
}
I want to pass the ArrayList to a activity which extends Fragment Activity.
I am sending the object like this
Intent intent = new Intent(
DashboardActivity.this,
BaseFragmentActivity.class);
intent.putParcelableArrayListExtra("dashBoardList", dashBoardList);
startActivity(intent);
And receiving it at the fragment activity like this
dashboardList = getIntent().getExtras().getParcelableArrayList("dashBoardList");
But still the intent is returninig me a null value... Pls help...
Try this:
private DashBoardPOJO(Parcel in) {
dashBoardName = in.readString();
dashBoardId = in.readString();
dashBoardDataList = (ArrayList<DashBoardDataPOJO>) in.readArrayList(DashBoardDataPOJO.class.getClassLoader());
}