RetroFit/GSON 返回的列表似乎具有量子力学特性

List Returned By RetroFit/GSON Seems to Have Quantum Mechanical Properties

我正在使用 RetroFit 1.9 和一个 GSON 转换器,到目前为止它对我来说运行良好。现在我正在尝试整理 Callback 中收到的自定义 Parcelable object 的 List,我遇到了 ClassCastException:

02-02 09:53:49.921 13030-13030/com.example.app E/AndroidRuntime: FATAL EXCEPTION: main
            Process: com.example.app, PID: 13030
            java.lang.ClassCastException: com.google.gson.internal.LinkedTreeMap cannot be cast to android.os.Parcelable
                at android.os.Parcel.writeTypedList(Parcel.java:1166)
                at com.example.common.util.ParcelableUtil.marshall(ParcelableUtil.java:37)
                at com.example.app.service.WearableMessageService.success(WearableMessageService.java:130)
                at com.example.app.service.WearableMessageService.success(WearableMessageService.java:120)
                at retrofit.CallbackRunnable.run(CallbackRunnable.java:45)
                at android.os.Handler.handleCallback(Handler.java:739)
                at android.os.Handler.dispatchMessage(Handler.java:95)
                at android.os.Looper.loop(Looper.java:148)
                at android.app.ActivityThread.main(ActivityThread.java:5417)
                at java.lang.reflect.Method.invoke(Native Method)
                at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
                at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)

这是我的回调方法:

new Callback<List<MyObject>>() {
    @Override
    public void success(List<MyObject> objects, Response response) {
        mByteArray = ParcelableUtil.marshall(objects);
    }

    @Override
    public void success(RetrofitError error) {
        Timber.w("Failed to retrieve events, with message " + error.getMessage());
    }
}

所以我在 success 方法中添加了一个 for 循环,就在我调用 ParcelableUtil.marshall() 之前,以测试为什么我得到 LinkedTreeMap object 的列表而不是我的 objects:

for(MyObject object : objects) {
    Timber.d(object.getTitle());
}

这不仅打印了每个 object 的正确标题,奇迹般地,其余的代码也能正常工作! ParcelableUtil 不再抛出错误,我收到一个字节数组,稍后我可以完美地解组。

一个LinkedTreeMapobject的列表在循环观察后如何变成我的object的列表?为什么我首先得到 LinkedTreeMap object 的列表?这里发生了什么?


MyObject class:

public class MyObject implements Parcelable {

    @SerializedName("title") private String mTitle;
    @SerializedName("location") private String mLocation;

    @SerializedName("start_date") private Date mStartDate;
    @SerializedName("end_date") private Date mEndDate;

    public MyObject() {
        /* Required empty constructor */
    }

    public Event(Parcel in) {
        mTitle = in.readString();
        mLocation = in.readString();

        mStartDate = new Date(in.readLong());
        mEndDate = new Date(in.readLong());
    }

    public String getTitle() {
        return mTitle;
    }

    public String getLocation() {
        return mLocation;
    }

    public void getStartDate() {
        return mStartDate();
    }

    public void getEndDate() {
        return mEndDate();
    }

    @Override
    public in describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(mTitle);
        dest.writeString(mLocation);

        dest.writeLong(mStartDate.getTime());
        dest.writeLong(mEndDate.getTime());
    }

    public static final Creator<MyObject> CREATOR = new Creator<MyObject>() {
        @Override
        public MyObject createFromParcel(Parcel in) {
            return new MyObject(in);
        }

        @Override
        public MyObject[] newArray(int size) {
            return new MyObject[size];
        }
    };
}

MyObject 的示例 JSON 供稿:

{
    "items": [
        {
            "title":"Object 1"
            "location":"Location 1"
            "start_date":"2016-02-02 15:30:00"
            "end_date":"2016-02-02 19:00:00"
        }
        {
            "title":"Object 2"
            "location":"Location 2"
            "start_date":"2016-02-02 18:00:00"
            "end_date":"2016-02-03 18:00:00"
        }
    ]
}

正在使用的 GSON TypeAdapterFactory

public class DataAdapterFactory implements TypeAdapterFactory {

    @Override
    public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {

        final TypeAdapter<T> delegate = gson.getDelegateAdapter(this, type);
        final TypeAdapter<JsonElement> elementAdapter = gson.getAdapter(JsonElement.class);

        return new TypeAdapter<T>() {

            @Override
            public void write(JsonWriter out, T value) throws IOException {
                delegate.write(out, value);
            }

            @Override
            public void read(JsonReader in) throws IOException {
                JsonElement jsonElement = elementAdapter.read(in);

                if(jsonElement.isJsonObject()) {
                    JsonObject jsonObject = jsonElement.getAsJsonObject();

                    if(jsonObject.has("items") && jsonObject.get("items".isJsonArray()) {
                        jsonElement = jsonObject.getAsJsonArray("items");
                    }

                    return delegate.fromJsonTree(jsonElement);
                }
            }
        }.nullSafe();
    }
}

啊,ProGuard,我存在的祸根...

今天早上我恍然大悟。 ProGuard 可能一直在删除 class,并且在 for 循环中使用 class 一定告诉 ProGuard 实际上需要 class。我将以下内容添加到我的 ProGuard 规则中,它现在无需循环即可运行:

-keep public class com.example.common.model.MyObject
-keep public class * implements com.example.common.model.MyObject
-keepclassmembers class com.example.common.model.MyObject {
    <methods>;
}

我相信在我的情况下我只需要第一行,但我认为其他的就可以覆盖我的基础。