Android:Realm + Retrofit 2 + Gson [英] Android: Realm + Retrofit 2 + Gson

查看:121
本文介绍了Android:Realm + Retrofit 2 + Gson的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用 Retrofit + Gson Realm 时遇到问题。我知道这3个库的组合存在问题。一些解决方案建议为 Gson 设置 ExclusionStrategy 可以解决这个问题,并且我尝试了它,但它不起作用。



我的代码如下所示:

  public class ObjectList {
public List< AnotherObject> anotherObject;
}

公共类AnotherObject扩展RealmObject {
private String propA;
public void setPropA(String propA){
this.setPropA = propA
}
public String getPropA(){
return propA
} $ b $ 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,连接到服务器失败,请检查您的连接,Toast.LENGTH_LONG).show();
}
});

使用当前的代码,我仍然会发生内存泄漏。这个代码有什么建议吗?



我的json结构如下所示:

  {anotherObject:[{propA:someValue}]} 


解决方案


为什么在编写 Gson
Realm 时要编写所有这些自定义序列化器只是一行代码


TL; DR。



您可以通过将非托管 RealmObjects 传递给您的 Retrofit 调用来解决此问题。



如果您不想完全解答所有问题,请跳至下面的推荐解决方案部分。

长谈话(详细回答)

这与改造。如果您已将 Gson 设置为当前的 Retrofit 实例的数据转换器,那么您可以确定它是 谁失败。 p>

假设我们有这个模型:

  public class Model extends RealmObject {
@PrimaryKey
long id;
布尔幸福;

public Model(){/ * Realm和Gson都需要* /}

public模型(long id,boolean happy){
this.id = ID;
this.happy =开心;
}

public long getId(){
return id;
}

public boolean isHappy(){
return happy;




$ b $ p
$ b

对于这段代码,我们没有问题:

  Model unmanagedModel = new Model(5,true); // unmanagedModel 
new Gson()。toJson(unmanagedModel); // {id:5,happy:true}

但是对于这一个:

 领域realm = /*...*/; 
Model managedModel = realm.copyToRealm(unmanagedModel);
new Gson()。toJson(managedModel); // {id:0,happy:false}

//我们会得到同样的代码
Model anotherManagedModel = realm.where(Model.class).equalTo(id ,5).findFirst();
new Gson()。toJson(anotherManagedModel); // {id:0,happy:false}

我们会很惊讶。我们看到 nulls 无处不在!



为什么?



Gson 仅当它是托管时才会序列化 RealmObject 。这意味着目前有一个打开的 Realm 实例,确保这个 RealmObject 反映了当前持久层中的内容( Realm 数据库)。



这种情况发生的原因是由于 Gson Realm 工作。为什么 Gson 看到 null 引用 Zhuinden >无处不在:


...这是因为GSON试图通过反射读取
Realm对象的字段,但是要获得值,您需要使用
访问器方法 - 它们通过Realm-transformer自动应用于代码中的所有字段访问
,但反射在任何地方都会看到空值
...
p>

Christian Melchior 提议解决方法通过编写自定义 JsonSerializers 分配给每个创建的 Model 。这是您使用的解决方法,但我不会推荐它。正如您已经意识到的那样,它需要编写错误和最糟糕的代码杀死 Gson code>是关于(这使我们的生活变得更加痛苦)。



推荐的解决方案



如果我们可以确保 realmObject 我们传给Gson不是托管

解决方案1 ​​

获取托管RealmObject的内存副本并将其传递给 Gson

  new Gson()。的toJSON(realm.copyFromRealm(managedModel)); 

解决方案2

(包装第一个解决方案)。如果第一个解决方案对你来说太冗长了,那么让你的模型看起来像这样:

  public class Model extends RealmObject {
@PrimaryKey
long id;
布尔幸福;

//一些方法...

public Model toUnmanaged(Realm realm){
return isManaged()? realm.copyFromRealm(this):this;


$ / code>

然后,你可以这样做:

  //当你确定你的模型是unmanged时,你可以传递null 
new Gson()。toJson(model .toUnmanaged(境界));

解决方案3

如果(像我一样)认为在执行 nulls 或要求 Realm 时,一些序列化,那么你可以去克隆你的模型。没有 Realm 需要实例!



已发布 here (向下滚动并查找@ AnixPasBesoin的帖子)。



1 - 创建一个通用接口CloneableRealmObject:

 接口CloneableRealmObject< T> {
T cloneRealmObject();



$ b 2让你的realmObjetcs像这样实现上述接口:

  public class Model extends RealmObject implements CloneableRealmObject< Model> {
@PrimaryKey
long id;

public Model(){
// Realm所需的空构造函数。

$ b @Override
public Model cloneRealmObject(){
Model clone = new Model();
clone.id = this.id;
return clone;






3 - 在传递给您的Retrofit调用之前克隆对象。

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

在最近的帖子中



我给出了一个答案,解释了为什么我们在使用 managed realmObjects 。我建议你看一看。



奖金



还希望检查由 RealmFieldNamesHelper /用户/ 1389357 / christian-melchior> Christian Melchior 使Realm查询更安全。


I have a problem when using Retrofit + Gson and Realm. I know that there is an issue with the combination of these 3 libraries. Some answers suggest that setting an ExclusionStrategy for Gson can solve this issue, and I tried it but it didn't work.

My code looks like:

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();
        }
    });

With the current code, I'm still getting the memory leak. Is there any suggestion for this code?

My json structure looks like:

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

解决方案

Why writing all these custom serializers when you can make Gson and Realm work together with just ONE LINE OF CODE?

TL;DR.

You can simply solve this by passing unmanaged RealmObjects to your Retrofit calls.

If you don't want to go through all this answer, then skip to the "Recommended solutions" section posted down below.

Long talk (verbose answer)

This has nothing to do with Retrofit. If you have set Gson to be the data converter to your current Retrofit instance, then you can be sure that it's Gson who's failing.

Suppose we have this Model:

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

For this code, we'll have no issue:

Model unmanagedModel = new Model(5, true); // unmanagedModel
new Gson().toJson(unmanagedModel);   // {id : 5, happy : true}

But for this one:

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}

We'll be surprised. We're seeing nulls everywhere!.

Why?

Gson fails serializing a RealmObject only if it's a managed one. Which means that there's currently an opened Realm instance making sure this RealmObject is reflecting what is currently held in the persistence layer (the Realm database).

The reason why this is happening is due to the conflicting nature of how both Gson and Realm work. Quoting Zhuinden on why Gson sees null everywhere:

... that's because GSON tries to read the fields of the Realm object via reflection, but to obtain the values, you need to use accessor methods - which are automatically applied to all field access in the code via the Realm-transformer, but reflection still sees nulls everywhere...

Christian Melchior proposes a workaround to this conflict by writing a custom JsonSerializers to every created Model. This is the workaround you have used, but I would NOT recommend it. As you have realized, it requires writing a lot of code which is error prone and the worst of all, kills what Gson is about (which is making our life less painful).

Recommended solutions

If we can somehow make sure the realmObject we pass to Gson is not a managed one, we'll avoid this conflict.

Solution 1

Get a copy in memory of the managed RealmObject and pass it to Gson

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

Solution 2

(Wrapping the 1st solution). If the 1st solution is too verbose for you, make your models look like this one:

public class Model extends RealmObject {
    @PrimaryKey
    long id;
    boolean happy;

    // Some methods ...

    public Model toUnmanaged(Realm realm) {
        return isManaged() ? realm.copyFromRealm(this) : this;
    }
}

And then, you can do something like this:

// You can pass null when you're sure your model is unmanged
new Gson().toJson(model.toUnmanaged(realm));

Solution 3

If (like me) you think it makes it ugly to pass nulls or to require a Realm when doing some serialization, then you can go with cloning your models. No Realm instance needed!

Already posted here (scroll down and look for @AnixPasBesoin's post).

1 - Create a generic interface CloneableRealmObject:

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

2 - Make your realmObjetcs implement the above interface like so:

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 the object before passing to your Retrofit calls.

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

In a recent post

I gave an answer explaining why we're getting this weird serialized output when using managed realmObjects. I recommend you to take a look at it.

Bonus

You might also want to check RealmFieldNamesHelper, a library made by Christian Melchior "to make Realm queries more type safe".

这篇关于Android:Realm + Retrofit 2 + Gson的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆