使用Retrofit + Gson和Realm时遇到问题.我知道这3个库的组合存在问题.一些答案表明,ExclusionStrategy
为Gson设置一个可以解决这个问题,我试过但它没有用.
我的代码看起来像:
public class ObjectList { public ListanotherObject; } 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 () { @Override public void onResponse(Response 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"}]}
Anix PasBeso.. 13
TL; DR.为什么要编写所有这些自定义序列化程序,以便让Gson和 Realm只与一行代码一起工作?
你可以通过将unmanaged传递RealmObjects
给你的Retrofit调用来解决这个问题.
如果您不想完成所有这些答案,请跳至下面发布的"推荐解决方案"部分.
冗长的谈话(冗长的回答)这与Retrofit无关.如果您已将Gson设置为当前Retrofit实例的数据转换器,那么您可以确定它是Gson失败的人.
假设我们有这个模型:
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
到处都看到了!
RealmObject
如果Gson是托管的,那么Gson就无法序列化.这意味着当前有一个打开的Realm
实例,确保它RealmObject
反映了当前持久层(Realm
数据库)中的内容.
之所以发生这种情况,是因为Gson和Realm的工作方式存在冲突.引用Zhuinden为什么Gsonnull
到处都看到了:
...这是因为GSON尝试通过反射读取Realm对象的字段,但是要获取值,您需要使用访问器方法 - 它们通过Realm-transformer自动应用于代码中的所有字段访问,但反射到处都看到空值...
Christian Melchior通过为每个创建者编写一个自定义来提出解决此冲突的方法.这是您使用过的解决方法,但我不推荐它.正如你已经意识到的那样,它需要编写大量易于出错的代码,而最糟糕的是,它会杀死所谓的内容(这会让我们的生活变得更加痛苦).JsonSerializers
Model
Gson
如果我们能以某种方式确保realmObject
我们传递给Gson不是managed
一个,我们将避免这种冲突.
获取托管RealmObject的内存副本并将其传递给Gson
new Gson().toJson(realm.copyFromRealm(managedModel));
(包装第一个解决方案).如果第一个解决方案对你来说太冗长,那么让你的模型看起来像这样:
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());
如果(像我一样)你认为通过它managed
或者realmObjects
在进行某些序列化时要求它是丑陋的,那么你可以去克隆你的模型.不需要RealmObjects
实例!
已经发布在这里(向下滚动并查找@ AnixPasBesoin的帖子).
1 - 创建一个通用接口CloneableRealmObject:
interface CloneableRealmObject{ T cloneRealmObject(); }
2 - 让你的realmObjetcs实现上面的界面,如下所示:
public class Model extends RealmObject implements CloneableRealmObject{ @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());在最近的一篇文章中
我给出了一个答案,解释了为什么我们在使用时得到这个奇怪的序列化输出nulls
RealmObject
.我建议你看一下.
您可能还想检查RealmFieldNamesHelper,这是一个由Christian Melchior 创建的库,"使Realm查询更安全".
TL; DR.为什么要编写所有这些自定义序列化程序,以便让Gson和 Realm只与一行代码一起工作?
你可以通过将unmanaged传递RealmObjects
给你的Retrofit调用来解决这个问题.
如果您不想完成所有这些答案,请跳至下面发布的"推荐解决方案"部分.
冗长的谈话(冗长的回答)这与Retrofit无关.如果您已将Gson设置为当前Retrofit实例的数据转换器,那么您可以确定它是Gson失败的人.
假设我们有这个模型:
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
到处都看到了!
RealmObject
如果Gson是托管的,那么Gson就无法序列化.这意味着当前有一个打开的Realm
实例,确保它RealmObject
反映了当前持久层(Realm
数据库)中的内容.
之所以发生这种情况,是因为Gson和Realm的工作方式存在冲突.引用Zhuinden为什么Gsonnull
到处都看到了:
...这是因为GSON尝试通过反射读取Realm对象的字段,但是要获取值,您需要使用访问器方法 - 它们通过Realm-transformer自动应用于代码中的所有字段访问,但反射到处都看到空值...
Christian Melchior通过为每个创建者编写一个自定义来提出解决此冲突的方法.这是您使用过的解决方法,但我不推荐它.正如你已经意识到的那样,它需要编写大量易于出错的代码,而最糟糕的是,它会杀死所谓的内容(这会让我们的生活变得更加痛苦).JsonSerializers
Model
Gson
如果我们能以某种方式确保realmObject
我们传递给Gson不是managed
一个,我们将避免这种冲突.
获取托管RealmObject的内存副本并将其传递给Gson
new Gson().toJson(realm.copyFromRealm(managedModel));
(包装第一个解决方案).如果第一个解决方案对你来说太冗长,那么让你的模型看起来像这样:
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());
如果(像我一样)你认为通过它managed
或者realmObjects
在进行某些序列化时要求它是丑陋的,那么你可以去克隆你的模型.不需要RealmObjects
实例!
已经发布在这里(向下滚动并查找@ AnixPasBesoin的帖子).
1 - 创建一个通用接口CloneableRealmObject:
interface CloneableRealmObject{ T cloneRealmObject(); }
2 - 让你的realmObjetcs实现上面的界面,如下所示:
public class Model extends RealmObject implements CloneableRealmObject{ @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());在最近的一篇文章中
我给出了一个答案,解释了为什么我们在使用时得到这个奇怪的序列化输出nulls
RealmObject
.我建议你看一下.
您可能还想检查RealmFieldNamesHelper,这是一个由Christian Melchior 创建的库,"使Realm查询更安全".