【问题标题】:Android: Realm + Retrofit 2 + Gson安卓:Realm + Retrofit 2 + Gson
【发布时间】:2016-03-17 18:35:08
【问题描述】:

我在使用 Retrofit + GsonRealm 时遇到问题。我知道这三个库的组合存在问题。一些答案表明为 Gson 设置一个ExclusionStrategy 可以解决这个问题,我尝试过但没有成功。

我的代码如下:

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

使用当前代码,我仍然遇到内存泄漏。对这段代码有什么建议吗?

我的 json 结构如下:

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

【问题讨论】:

  • 不清楚您遇到了什么问题?什么内存泄漏?
  • 你怎么得到response.body().anotherObject;?你应该使用response.body().get(0..); ?
  • 嗨@Ewoks 感谢您的回复..目前我不再使用android了..
  • @cana 请不要再在这种状态下留下问题。当其他用户登陆这里时,他们只是因为模糊的问题描述和诸如“我刚刚解决了它,不再重要”之类的问题而浪费时间
  • @cana 你能提供你的答案吗?这对 stackoverlofw 上的其他用户有点不公平。这是关于获得帮助和帮助。

标签: java android gson realm retrofit


【解决方案1】:

既然可以制作Gson,为什么还要编写所有这些自定义序列化程序? Realm 只与 一行代码 一起工作?

TL;DR.

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

如果您不想回答所有这些问题,请跳至下方发布的“推荐解决方案”部分。

长篇大论(详细回答)

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

假设我们有这个模型:

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

对于这段代码,我们不会有任何问题:

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

但是对于这个:

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}

我们会感到惊讶。我们到处都能看到nulls!。

为什么?

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

发生这种情况的原因是由于 GsonRealm 工作方式的冲突性质。引用 Zhuinden 关于为什么 Gson 到处都能看到 null

...这是因为 GSON 尝试读取 Realm 对象通过反射,但要获取值,您需要使用 访问器方法 - 自动应用于所有字段访问 通过 Realm-transformer 在代码中,但反射仍然看到空值 无处不在……

Christian Melchior 通过将自定义JsonSerializers 写入每个创建的Model 来针对此冲突提出workaround。这是您使用的解决方法,但我不推荐它。正如您所意识到的,它需要编写大量代码,这容易出错,最糟糕的是,扼杀了Gson 的意义 (这让我们的生活不再那么痛苦)。

推荐的解决方案

如果我们能以某种方式确保我们传递给 Gson 的 realmObject 不是 managed,我们将避免这种冲突。

解决方案 1

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

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

解决方案 2

(包装第一个解决方案)。如果第一种解决方案对您来说过于冗长,请让您的模型看起来像这样:

public class Model extends RealmObject {
    @PrimaryKey
    long id;
    boolean happy;
    
    // Some methods ...

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

然后,您可以执行以下操作:

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

解决方案 3

这个不是很实用,但值得一提。您可以对模型进行深度克隆(取自 here)。

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

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

2 - 让你的 realmObjetcs 像这样实现上面的接口:

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 - 在传递给您的 Retrofit 调用之前克隆对象。

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

在最近的一篇文章中

我给了一个answer,解释了为什么我们在使用managedrealmObjects 时会得到这个奇怪的序列化输出。我建议你看看它。

奖金

您可能还想查看RealmFieldNamesHelper,这是一个由Christian Melchior 制作的库,“以使 Realm 查询更安全”。

【讨论】:

  • Retrofit 默认不使用 Gson。您必须明确设置数据转换器。
  • @cricket_007 你是对的。我会编辑我的问题。但是,不知何故,Gson 是最常用的数据转换器。
  • 因为最小的文档只是给出了如何使用 Retrofit 的复制粘贴版本。有些人将它与杰克逊一起使用
  • @cricket_007 杰克逊是否克服了这些领域的限制?我以前从未使用过它,所以我不知道它的限制。
  • 还没用过Realm,所以不知道
【解决方案2】:

我也遇到了类似的问题。这是因为您的请求格式错误。就我而言,我试图通过从本地 SQLite DB 而不是 Java 对象获取 Realm 对象来发送它。 Retrofit 只将 Java 对象转换为 JSON,而不是 Realm 对象。请确保在使用 Retrofit 时发送正确的 JSON 作为请求。

然后我替换了这个:

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

收件人:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-03-16
    • 1970-01-01
    • 1970-01-01
    • 2017-01-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多