ORMLite 5 和 Retrofit 2 的递归对象序列化问题
Recursive Objects Serialization issue with ORMLite 5 and Retrofit 2
我有 2 个 类 像下面的代码示例:
Class一个
@DatabaseField(id = true, columnName = "id")
private UUID id;
@ForeignCollectionField
private LazyForeignCollection<B, UUID> bList;
Class B
@DatabaseField(id = true)
private UUID id;
@DatabaseField(columnName = "idA", foreign = true)
private A objA;
@DatabaseField
private String foo;
我在使用 Retrofit2 将此数据从 android 发送到 API 时遇到问题,因为当我发送 A 时,序列化程序调用 bList 并且 ORMLite 自动加载它。
我的 bList 中 B 的每个对象都已 "objA" 填充,并且每个 "objA" 都有一个包含数据的 bList。
它对应用程序没问题,因为它总是分配相同的对象,但 GSon 序列化程序尝试序列化每个级别并抛出此异常:
ERR: stack=java.lang.RuntimeException: An error occured while executing doInBackground()
at android.os.AsyncTask.done(AsyncTask.java:304)
at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:355)
at java.util.concurrent.FutureTask.setException(FutureTask.java:222)
at java.util.concurrent.FutureTask.run(FutureTask.java:242)
at android.os.AsyncTask$SerialExecutor.run(AsyncTask.java:231)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
at java.lang.Thread.run(Thread.java:818)
Caused by: android.database.CursorWindowAllocationException: Cursor window allocation of 2048 kb failed. # Open Cursors=760 (# cursors opened by this proc=760)
at android.database.CursorWindow.<init>(CursorWindow.java:108)
at android.database.AbstractWindowedCursor.clearOrCreateWindow(AbstractWindowedCursor.java:198)
at android.database.sqlite.SQLiteCursor.fillWindow(SQLiteCursor.java:139)
at android.database.sqlite.SQLiteCursor.getCount(SQLiteCursor.java:133)
at android.database.AbstractCursor.moveToPosition(AbstractCursor.java:197)
at android.database.AbstractCursor.moveToFirst(AbstractCursor.java:237)
at com.j256.ormlite.android.AndroidDatabaseResults.first(AndroidDatabaseResults.java:68)
at com.j256.ormlite.android.AndroidDatabaseConnection.queryForOne(AndroidDatabaseConnection.java:206)
at com.j256.ormlite.stmt.mapped.MappedQueryForFieldEq.execute(MappedQueryForFieldEq.java:38)
at com.j256.ormlite.field.FieldType.createForeignObject(FieldType.java:1047)
at com.j256.ormlite.field.FieldType.assignField(FieldType.java:556)
at com.j256.ormlite.stmt.mapped.BaseMappedQuery.mapRow(BaseMappedQuery.java:72)
at com.j256.ormlite.stmt.SelectIterator.getCurrent(SelectIterator.java:284)
at com.j256.ormlite.stmt.SelectIterator.nextThrow(SelectIterator.java:168)
at com.j256.ormlite.stmt.SelectIterator.next(SelectIterator.java:181)
at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.write(CollectionTypeAdapterFactory.java:96)
at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.write(CollectionTypeAdapterFactory.java:61)
at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:68)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.write(ReflectiveTypeAdapterFactory.java:112)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:239)
at com.google.gson.Gson$FutureTypeAdapter.write(Gson.java:968)
at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:68)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.write(ReflectiveTypeAdapterFactory.java:112)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:239)
at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:68)
at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.write(CollectionTypeAdapterFactory.java:97)
at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.write(CollectionTypeAdapterFactory.java:61)
at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:68)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.write(ReflectiveTypeAdapterFactory.java:112)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:239)
at com.google.gson.Gson$FutureTypeAdapter.write(Gson.java:968)
at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:68)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.write(ReflectiveTypeAdapterFactory.java:112)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:239)
at com.google.gson.internal.bind.TypeAdapterRuntime
还有这个:
09-11 09:59:16.618 28706-28747/br.com.igtech.nr18 D/Error: ERR: TOTAL BYTES WRITTEN: 1636884
09-11 09:59:16.618 28706-28747/br.com.igtech.nr18 E/JavaBinder: !!! FAILED BINDER TRANSACTION !!!
09-11 09:59:16.628 28706-28747/br.com.igtech.nr18 E/AndroidRuntime: Error reporting crash
android.os.TransactionTooLargeException
at android.os.BinderProxy.transactNative(Native Method)
at android.os.BinderProxy.transact(Binder.java:496)
at android.app.ActivityManagerProxy.handleApplicationCrash(ActivityManagerNative.java:4164)
at com.android.internal.os.RuntimeInit$UncaughtHandler.uncaughtException(RuntimeInit.java:89)
at java.lang.ThreadGroup.uncaughtException(ThreadGroup.java:693)
at java.lang.ThreadGroup.uncaughtException(ThreadGroup.java:690)
手动将对象设置为空可以解决这个问题,但我认为这不是最好的方法。
阅读this and this
解决了将 ConverterFactory 从 Gson 更改为 Jackson
retrofit = new Retrofit
.Builder()
.baseUrl(baseUrl)
.addConverterFactory(JacksonConverterFactory.create())
.client(okHttpClient)
.build();
Jackson 有解决循环问题的 @JsonManagedReference
和 @JsonBackReference
注释。
我有 2 个 类 像下面的代码示例:
Class一个
@DatabaseField(id = true, columnName = "id")
private UUID id;
@ForeignCollectionField
private LazyForeignCollection<B, UUID> bList;
Class B
@DatabaseField(id = true)
private UUID id;
@DatabaseField(columnName = "idA", foreign = true)
private A objA;
@DatabaseField
private String foo;
我在使用 Retrofit2 将此数据从 android 发送到 API 时遇到问题,因为当我发送 A 时,序列化程序调用 bList 并且 ORMLite 自动加载它。
我的 bList 中 B 的每个对象都已 "objA" 填充,并且每个 "objA" 都有一个包含数据的 bList。 它对应用程序没问题,因为它总是分配相同的对象,但 GSon 序列化程序尝试序列化每个级别并抛出此异常:
ERR: stack=java.lang.RuntimeException: An error occured while executing doInBackground()
at android.os.AsyncTask.done(AsyncTask.java:304)
at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:355)
at java.util.concurrent.FutureTask.setException(FutureTask.java:222)
at java.util.concurrent.FutureTask.run(FutureTask.java:242)
at android.os.AsyncTask$SerialExecutor.run(AsyncTask.java:231)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
at java.lang.Thread.run(Thread.java:818)
Caused by: android.database.CursorWindowAllocationException: Cursor window allocation of 2048 kb failed. # Open Cursors=760 (# cursors opened by this proc=760)
at android.database.CursorWindow.<init>(CursorWindow.java:108)
at android.database.AbstractWindowedCursor.clearOrCreateWindow(AbstractWindowedCursor.java:198)
at android.database.sqlite.SQLiteCursor.fillWindow(SQLiteCursor.java:139)
at android.database.sqlite.SQLiteCursor.getCount(SQLiteCursor.java:133)
at android.database.AbstractCursor.moveToPosition(AbstractCursor.java:197)
at android.database.AbstractCursor.moveToFirst(AbstractCursor.java:237)
at com.j256.ormlite.android.AndroidDatabaseResults.first(AndroidDatabaseResults.java:68)
at com.j256.ormlite.android.AndroidDatabaseConnection.queryForOne(AndroidDatabaseConnection.java:206)
at com.j256.ormlite.stmt.mapped.MappedQueryForFieldEq.execute(MappedQueryForFieldEq.java:38)
at com.j256.ormlite.field.FieldType.createForeignObject(FieldType.java:1047)
at com.j256.ormlite.field.FieldType.assignField(FieldType.java:556)
at com.j256.ormlite.stmt.mapped.BaseMappedQuery.mapRow(BaseMappedQuery.java:72)
at com.j256.ormlite.stmt.SelectIterator.getCurrent(SelectIterator.java:284)
at com.j256.ormlite.stmt.SelectIterator.nextThrow(SelectIterator.java:168)
at com.j256.ormlite.stmt.SelectIterator.next(SelectIterator.java:181)
at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.write(CollectionTypeAdapterFactory.java:96)
at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.write(CollectionTypeAdapterFactory.java:61)
at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:68)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.write(ReflectiveTypeAdapterFactory.java:112)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:239)
at com.google.gson.Gson$FutureTypeAdapter.write(Gson.java:968)
at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:68)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.write(ReflectiveTypeAdapterFactory.java:112)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:239)
at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:68)
at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.write(CollectionTypeAdapterFactory.java:97)
at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.write(CollectionTypeAdapterFactory.java:61)
at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:68)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.write(ReflectiveTypeAdapterFactory.java:112)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:239)
at com.google.gson.Gson$FutureTypeAdapter.write(Gson.java:968)
at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:68)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.write(ReflectiveTypeAdapterFactory.java:112)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:239)
at com.google.gson.internal.bind.TypeAdapterRuntime
还有这个:
09-11 09:59:16.618 28706-28747/br.com.igtech.nr18 D/Error: ERR: TOTAL BYTES WRITTEN: 1636884
09-11 09:59:16.618 28706-28747/br.com.igtech.nr18 E/JavaBinder: !!! FAILED BINDER TRANSACTION !!!
09-11 09:59:16.628 28706-28747/br.com.igtech.nr18 E/AndroidRuntime: Error reporting crash
android.os.TransactionTooLargeException
at android.os.BinderProxy.transactNative(Native Method)
at android.os.BinderProxy.transact(Binder.java:496)
at android.app.ActivityManagerProxy.handleApplicationCrash(ActivityManagerNative.java:4164)
at com.android.internal.os.RuntimeInit$UncaughtHandler.uncaughtException(RuntimeInit.java:89)
at java.lang.ThreadGroup.uncaughtException(ThreadGroup.java:693)
at java.lang.ThreadGroup.uncaughtException(ThreadGroup.java:690)
手动将对象设置为空可以解决这个问题,但我认为这不是最好的方法。
阅读this and this
解决了将 ConverterFactory 从 Gson 更改为 Jackson
retrofit = new Retrofit
.Builder()
.baseUrl(baseUrl)
.addConverterFactory(JacksonConverterFactory.create())
.client(okHttpClient)
.build();
Jackson 有解决循环问题的 @JsonManagedReference
和 @JsonBackReference
注释。