Android: Realm + Retrofit 2 + Gson
Tengo un problema al usar Retrofit + Gson y Realm . Sé que hay un problema con la combinación de estas 3 bibliotecas. Algunas respuestas sugieren que establecer una ExclusionStrategy
para Gson puede resolver este problema, y lo probé pero no funcionó.
Mi código parece:
- Manejar Error de red con Retrofit observable
- Retrofit + OkHttp + GZIP-ed JSON
- Ajustar la variable json del mapa a la palabra clave
- Actualizaciones de Android Retrofit 2.0
- Retrofit Image Upload dar error
public class ObjectList { public List<AnotherObject> anotherObject; } public class AnotherObject extends RealmObject { private String propA; public void setPropA(String propA){ this.setPropA = propA } public String getPropA(){ return propA } } Gson gson = new GsonBuilder().setExclusionStrategies(new ExclusionStrategy() { @Override public boolean shouldSkipField(FieldAttributes f) { return f.getDeclaringClass().equals(RealmObject.class); } @Override public boolean shouldSkipClass(Class<?> clazz) { return false; } }).create(); Retrofit retrofit = new Retrofit.Builder() .baseUrl("http://localhost/api/") .addConverterFactory(GsonConverterFactory.create(gson)) .build(); ObjectAPI objectAPI = retrofit.create(ObjectAPI.class); call.enqueue(new Callback<ObjectList>() { @Override public void onResponse(Response<ObjectList> response, Retrofit retrofit) { objectList = response.body().anotherObject; onRefreshComplete(); } @Override public void onFailure(Throwable t) { Toast.makeText(context, "Connection to server failed, please check your connection", Toast.LENGTH_LONG).show(); } });
Con el código actual, sigo recibiendo la pérdida de memoria. ¿Hay alguna sugerencia para este código?
Mi estructura de json se parece a:
{"anotherObject":[{"propA": "someValue"}]}
- ¡Retrofit! Devolver un tipo genérico observable
- Cómo enviar datos JSON como Cuerpo usando Retrofit android
- Cookie de sesión no es persistente en Retrofit Android
- ¿Cómo manejar los campos JSON opcionales en Retrofit para Android?
- Retrofit: parámetros de consulta múltiples en el comando @GET?
- OkHttp / tiempo de espera de retroactivación predeterminado
- ¿Cuál es el razonamiento para separar los métodos RestAdapter.build () y .create () al usar Dagger?
- Retrofit2 Publicar imagen como base64
¿Por qué escribir todos estos serializadores personalizados cuando puede hacer que Gson y Realm trabajen juntos con sólo UNA LÍNEA DE CÓDIGO ?
TL; DR.
Puede solucionar esto simplemente pasando RealmObject
no RealmObject
a sus llamadas de Retrofit .
Si no quiere pasar por toda esta respuesta, vaya a la sección "Soluciones recomendadas" para ver cómo se puede hacer esto.
Versión detallada
Esto no tiene nada que ver con Retrofit . Si ha configurado Gson como el convertidor de datos para su instancia actual de Retrofit , asegúrese de que es Gson quien falla aquí.
Digamos que tenemos este modelo:
public class Model extends RealmObject { @PrimaryKey long id; boolean happy; public Model() {/* Required by both Realm and Gson*/} public Model(long id, boolean happy) { this.id = id; this.happy = happy; } public long getId() { return id; } public boolean isHappy() { return happy; } }
Para este código, no tendremos ningún problema:
Model unmanagedModel = new Model(5, true); // unmanagedModel new Gson().toJson(unmanagedModel); // {id : 5, happy : true}
Pero para este:
Realm realm = /*...*/; Model managedModel = realm.copyToRealm(unmanagedModel); new Gson().toJson(managedModel); // {id : 0, happy : false} // We'll get the samething for this code Model anotherManagedModel = realm.where(Model.class).equalTo("id",5).findFirst(); new Gson().toJson(anotherManagedModel); // {id : 0, happy : false}
Nos sorprenderemos. Estamos viendo nulls
todas partes !.
¿Por qué?
Gson falla en la serialización de un RealmObject
sólo si se trata de una gestión . Lo que significa que actualmente hay una instancia de Realm
abierta que asegura que RealmObject
refleja lo que actualmente se mantiene en la capa de persistencia (la base de datos Realm
).
La razón por la que esto sucede se debe a la naturaleza conflictiva de cómo trabajan tanto Gson como Realm . Citando Zhuinden sobre por qué Gson ve null
todas partes:
… es porque GSON intenta leer los campos del objeto Realm a través de la reflexión, pero para obtener los valores, necesitas usar métodos accessor – que se aplican automáticamente a todos los campos de acceso en el código a través del Realm-transformador, pero la reflexión Todavía ve nulos en todas partes …
Christian Melchior propone una solución a este conflicto escribiendo un JsonSerializers
personalizado para cada Model
creemos. Esta es la solución que has utilizado pero no lo recomendaría porque como te has dado cuenta, requiere escribir un montón de código que es propenso a errores y lo peor de todo, mata lo que Gson
se trata (que está haciendo nuestra vida menos dolorosa) .
Soluciones recomendadas
Si de alguna manera podemos asegurarnos de que el realmObject
que pasamos a Gson no es una gestión, no tendremos ningún problema.
Solución 1
Obtenga una copia en memoria del RealmObject administrado y envíelo a Gson
new Gson().toJson(realm.copyFromRealm(managedModel));
Solución 2
(Envoltura de la primera solución). Si la primera solución es demasiado detallada para usted, haga que sus modelos se parezcan a éste:
public class Model extends RealmObject { @PrimaryKey long id; boolean happy; // Some methods ... public Model toUnmanaged(Realm realm) { return isManaged() ? realm.copyFromRealm(this) : this; } }
Y entonces usted puede hacer algo como esto:
// You can pass null when you're sure your model is unmanged new Gson().toJson(model.toUnmanaged(realm));
Solución 3
Si no te gusta pasar nulls
o requerir un Realm
al hacer alguna serialización, puedes ir con la clonación de tus modelos. ¡No necesita instancia de Realm
!
Ya se ha publicado aquí (desplácese hacia abajo y busque el post de @AnixPasBesoin).
1 – Crear una interfaz genérica CloneableRealmObject:
interface CloneableRealmObject<T> { T cloneRealmObject(); }
2 – Haga que su realmObjetcs implemente la interfaz anterior de la siguiente manera:
public class Model extends RealmObject implements CloneableRealmObject<Model> { @PrimaryKey long id; public Model() { // Empty constructor required by Realm. } @Override public Model cloneRealmObject() { Model clone = new Model(); clone.id = this.id; return clone; } }
3 – Clonar el objeto antes de pasar a sus llamadas de Retrofit.
new Gson().toJson(model.cloneRealmObject());
En una publicación reciente
Le di una respuesta explicando por qué estamos recibiendo esta extraña salida serializada al usar realmObjects
administrados. Te recomiendo que lo eches un vistazo.
Prima
También es posible que desee comprobar RealmFieldNamesHelper , una biblioteca hecha por Christian Melchior "para que las consultas de Realm sean más seguras".
- Android LocationRequest: obtener una devolución de llamada cuando la solicitud caduca
- Anclaje inverso de Android sobre usb (no raíz)