【问题标题】:In fragment, restore an object that contains a list of another object with Parcelable implementation在片段中,使用 Parcelable 实现恢复包含另一个对象列表的对象
【发布时间】:2019-01-03 16:15:51
【问题描述】:

我有一个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];
        }
    };
}

在片段中,我尝试恢复名为 myStoreStore 实例,包括其中的汽车列表:

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 中出现问题,代码尝试在onCreate() 中恢复myStore。在运行时,恢复的myStore 确实在列表中包含一辆汽车,但该列表中的汽车具有奇怪的长值(例如28429333427126393)和model 字段中的null。为什么?我做错了什么?如何恢复一个对象包含另一个对象的列表?

【问题讨论】:

  • 我认为您的代码无法编译。 myStore = savedInstanceState.getString(MY_STORE) 将失败,因为您将 String 分配给 Store 类型的字段。
  • 谢谢,这只是我的错字,更新为getParcelable(MY_STORE)

标签: android android-fragments parcelable


【解决方案1】:

此问题通常发生在您的carList 实例为空,因此您在 parcelable 中编写了一个空对象并尝试从中读取。

其中一个选项是您在列表为空时对其进行初始化,然后将其写入 parcelable。使用这种情况,您的列表在恢复后总会有一个参考,这可能不是您想要的。

另一种方法是在将您的列表写入 parcelable 之前写入一个字节值,表明您的列表为空,或者您应该期望从中获得一些价值。对于这种情况,我使用this helper class 解决了我自己的问题。根据我的需要,我做了一些改进。如果您愿意,这是我的版本:

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

希望对你有帮助。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-02-17
    • 1970-01-01
    • 1970-01-01
    • 2011-11-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多