android - 在 fragment 中,恢复一个对象,该对象包含另一个具有 Parcelable 实现的对象的列表

标签 android android-fragments parcelable

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

在 fragment 中,我尝试恢复名为 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 确实包含列表中的一辆汽车,但该列表中的汽车具有奇怪的 long 值(例如 28429333427126393)和 nullmodel 字段中。为什么?我做错了什么?如何恢复一个对象包含另一个对象的列表?

最佳答案

此问题通常发生在您的 carList 时。 instance 为 null 所以你在 parcelable 中写了一个 null 对象并尝试从中读取。

其中一个选项是当列表为空时初始化列表,然后将其写入 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;
    }
}

希望对您有所帮助。

关于android - 在 fragment 中,恢复一个对象,该对象包含另一个具有 Parcelable 实现的对象的列表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54026032/

相关文章:

java - Android:自定义 Parcelable 类在没有抛出异常的情况下使应用程序崩溃

Kotlin:使用 parcelize 时的构造函数

java - 当我尝试读取通过 Intent 传递的 Vector 时出现 NullPointerException

java - 如何从 Android (Java) 中的位图中读取元数据?

android - 关于 inflater.inflate Android 文档的混淆

android - 通过 Intent 传递额外内容,同时将 Intent 与操作 ACTION_GET_CONTENT 一起使用

android-fragments - RecyclerView 滚动到位置并获取该 View

android - Viewpager 和 Sherlock Fragments。如何在 FragmentActivity 的 fragment 内部做一些事情?

android - 由于主项目的复杂库项目依赖性,Jenkins-Server 上的自动化 Android UI 测试失败

java - 根据角度和速度更新坐标