android gson retrofit realm

Android: Realm+Retrofit 2+Gson



(2)

¿Por qué escribir todos estos serializadores personalizados cuando puede hacer que Gson y Realm trabajen juntos con solo UNA LÍNEA DE CÓDIGO ?

TL; DR.

Simplemente puede resolver esto pasando RealmObjects no RealmObjects a sus llamadas de Retrofit .

Si no desea pasar por toda esta respuesta, salte a la sección "Soluciones recomendadas" publicada a continuación.

Charla larga (respuesta detallada)

Esto no tiene nada que ver con Retrofit . Si ha configurado a Gson para que sea el convertidor de datos a su instancia de Retrofit actual, entonces puede estar seguro de que es Gson quien falla.

Supongamos 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}

Estaremos sorprendidos ¡Estamos viendo nulls todas partes!

¿Por qué?

Gson no serializa un RealmObject solo si es administrado . Lo que significa que actualmente hay una instancia de Realm abierta que se asegura de que este RealmObject refleje lo que se encuentra actualmente en la capa de persistencia (la base de datos de Realm ).

La razón por la que esto está sucediendo se debe a la naturaleza conflictiva de cómo funcionan Gson y Realm . Citando a Zhuinden sobre por qué Gson ve null todas partes:

... eso es porque GSON intenta leer los campos del objeto Realm a través de la reflexión, pero para obtener los valores, debe usar métodos de acceso, que se aplican automáticamente a todos los accesos de campo en el código a través del transformador Realm, pero la reflexión todavía ve nulos por todas partes ...

Christian Melchior propone una workaround a este conflicto escribiendo un JsonSerializers personalizado para cada Model creado. Esta es la solución que ha utilizado, pero NO la recomendaría . Como te has dado cuenta, requiere escribir mucho código que es propenso a errores y lo peor de todo, mata de qué trata Gson (lo que hace que nuestra vida sea menos dolorosa).

Soluciones recomendadas

Si de alguna manera podemos asegurarnos de que el objeto realmObject que pasamos a Gson no sea managed , evitaremos este conflicto.

Solución 1

Obtenga una copia en la memoria del RealmObject administrado y páselo a Gson

new Gson().toJson(realm.copyFromRealm(managedModel));

Solución 2

(Envolviendo la primera solución). Si la primera solución es demasiado detallada para usted, haga que sus modelos se vean así:

public class Model extends RealmObject { @PrimaryKey long id; boolean happy; // Some methods ... public Model toUnmanaged() { return isManaged() ? getRealm().copyFromRealm(this) : this; } }

Y luego, puedes hacer algo como esto:

// always convert toUnmanaged when serializing new Gson().toJson(model.toUnmanaged());

Solución 3

Este NO es muy práctico pero vale la pena mencionarlo. Puede ir con clonación profunda de sus modelos.

Ya publicado here (desplácese hacia abajo y busque la publicación de @ AnixPasBesoin).

1 - Crear una interfaz genérica CloneableRealmObject:

interface CloneableRealmObject<T> { T cloneRealmObject(); }

2 - Haga que su realObjetcs 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 - Clone el objeto antes de pasarlo a sus llamadas de Retrofit.

new Gson().toJson(model.cloneRealmObject());

En una publicación reciente

realmObjects una answer explicando por qué estamos obteniendo esta salida serializada extraña cuando usamos realmObjects managed . Te recomiendo que lo eches un vistazo.

Prima

También puede consultar RealmFieldNamesHelper , una biblioteca creada por Christian Melchior "para hacer que las consultas de Realm sean más seguras".

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 configurar una ExclusionStrategy de ExclusionStrategy para Gson puede resolver este problema, y ​​lo intenté pero no funcionó.

Mi código se ve así:

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 teniendo la pérdida de memoria. ¿Hay alguna sugerencia para este código?

Mi estructura json se ve así:

{"anotherObject":[{"propA": "someValue"}]}


Yo también enfrenté el problema similar. Esto se debe a que su formato de solicitud es incorrecto. En mi caso, estoy tratando de enviar un objeto Realm obteniéndolo de SQLite DB local en lugar de un objeto Java. Retrofit convierte solo objetos Java a JSON pero no objetos Realm. Asegúrese de enviar un JSON correcto como solicitud cuando use Retrofit.

Luego reemplacé esto:

List<MyRealmModel> objectsToSync = mRealm.where(MyRealmModel.class).findAll();

A:

List<MyRealmModel> objectsToSend = mRealm.copyFromRealm(objectsToSync);