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
不再抛出错误,我收到一个字节数组,稍后我可以完美地解组。
一个LinkedTreeMap
object的列表在循环观察后如何变成我的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>;
}
我相信在我的情况下我只需要第一行,但我认为其他的就可以覆盖我的基础。
我正在使用 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
不再抛出错误,我收到一个字节数组,稍后我可以完美地解组。
一个LinkedTreeMap
object的列表在循环观察后如何变成我的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>;
}
我相信在我的情况下我只需要第一行,但我认为其他的就可以覆盖我的基础。