使用 Retrofit + Gson 和 Realm 时遇到问题。我知道这3个库的组合存在问题。一些答案表明,为 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"}]}
答案 0 :(得分:13)
为什么要在编写 Gson 和时编写所有这些自定义序列化程序 Realm 与一行代码一起工作?
您可以通过将非托管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
!
Gson 只有在{em>托管的情况下才会序列化RealmObject
。这意味着当前有一个打开的Realm
实例,确保此RealmObject
反映了持久层(Realm
数据库)中当前持有的内容。
之所以发生这种情况,是因为 Gson 和 Realm 的工作方式存在冲突。引用Zhuinden为什么 Gson 在任何地方看到null
:
...那是因为GSON试图读取的字段 Realm对象通过反射,但要获取值,需要使用 存取方法 - 自动应用于所有字段访问 在代码中通过Realm-transformer,但反射仍然看到空值 无处不在...
Christian Melchior通过向每个已创建的JsonSerializers
编写自定义Model
,为此冲突提出workaround。这是您使用过的解决方法,但我不推荐。正如您所知,它需要编写大量代码 容易出错,最糟糕的是,会杀死Gson
所关注的内容强>(这使我们的生活减少痛苦)。
如果我们能够以某种方式确保我们传递给Gson的realmObject
不是managed
,我们将避免这种冲突。
获取托管RealmObject的内存副本并将其传递给 Gson
new Gson().toJson(realm.copyFromRealm(managedModel));
(包装第一个解决方案)。如果第一个解决方案对您来说过于冗长,那么让您的模型看起来像这样:
public class Model extends RealmObject {
@PrimaryKey
long id;
boolean happy;
// Some methods ...
public Model toUnmanaged(Realm realm) {
return isManaged() ? realm.copyFromRealm(this) : this;
}
}
然后,你可以这样做:
// You can pass null when you're sure your model is unmanged
new Gson().toJson(model.toUnmanaged(realm));
如果(像我一样)你认为在进行某些序列化时传递nulls
或要求Realm
会很难看,那么你可以继续克隆你的模型。不需要Realm
个实例!
已发布here(向下滚动并查找@ AnixPasBesoin的帖子)。
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解释了为什么我们在使用managed
realmObjects
时获得这种奇怪的序列化输出。我建议你看一下。
您可能还想检查RealmFieldNamesHelper,Christian Melchior创建的库,以使Realm查询更安全“。
答案 1 :(得分:0)
我也面临类似的问题。这是因为您的请求格式不正确。就我而言,我试图通过从本地SQLite DB而非Java对象获取来发送Realm对象。改造仅将Java对象转换为JSON,而不将Realm对象转换。使用Retrofit时,请确保发送正确的JSON作为请求。
然后我替换了它:
List<MyRealmModel> objectsToSync = mRealm.where(MyRealmModel.class).findAll();
收件人:
List<MyRealmModel> objectsToSend = mRealm.copyFromRealm(objectsToSync);