Во фрагменте восстановите объект, который содержит список другого объекта с реализацией Parcelable. - PullRequest
0 голосов
/ 03 января 2019

У меня есть класс Car, реализующий класс 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];
        }
    };
}

Затем у меня есть класс Store, который содержит список автомобилей, он также реализует 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];
        }
    };
}

Во фрагменте я пытаюсь восстановить экземпляр Store с именем myStore, включая список машин в нем:

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);
    }

}

Проблема возникает, когда в MyFragment, кодпытается восстановить myStore в onCreate().Во время выполнения восстановленный myStore содержит один автомобиль в списке, но автомобиль в этом списке имеет идентификатор со странным длинным значением (например, 28429333427126393) и null в поле model.ЗАЧЕМ?Что я сделал не так?Как восстановить объект, содержащий список другого объекта?

1 Ответ

0 голосов
/ 03 января 2019

Эта проблема обычно возникает, когда ваш carList экземпляр был равен нулю , поэтому вы написали нулевой объект в parcelable и попытались прочитать его.

Один из вариантов заключается в том, что вы инициализируете свой список, когда он равен нулю, а затем записываете его в parcelable. В этом случае в вашем списке после восстановления всегда будет ссылка, которая может не соответствовать вашей.

Другой подход состоит в том, чтобы записать байтовое значение перед записью вашего списка в parcelable, указывая, что ваш список был нулевым или вы должны ожидать какое-то значение от него. Для этого случая я использую этот вспомогательный класс , который решил мою собственную проблему. Исходя из моих потребностей, я сделал некоторые улучшения. Вот моя версия, если вы предпочитаете ее использовать:

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;
    }
}

Надеюсь, это поможет.

...