【问题标题】:Cannot retrieve field values from realm object, values are null in debugger无法从领域对象中检索字段值,调试器中的值为空
【发布时间】:2016-05-20 16:37:34
【问题描述】:

我的 RealmObject 值似乎被 RealmProxy 类隐藏了,但可以从代理类中设置。

如您所见,我的模型非常简单。

public class GroupRealm extends RealmObject {
    @PrimaryKey
    public String id;
    @Index
    public String name;
    public String imageUrl;
    public int order;

    public GroupRealm parent;
    public RealmList<GroupRealm> children;
    public RealmList<ContentRealm> contents;
}

这就是我设置值的方式(db 是一个有效的领域,一切都在一个可以正常提交的事务中):

GroupRealm gr = db.where(GroupRealm.class).equalTo("id",g.GroupID).findFirst();
        if(gr==null){
            gr = db.createObject(GroupRealm.class,g.GroupID);
        }
        gr.imageUrl = g.GlyphUrl;
        gr.name = g.Title;
        gr.order = g.OrderNum;

下图是我在后面查询db时得到的。(相同的变量名在代码中不同的地方)

在我定义了 RealmObjects 的项目的 android.library 中,我有必要的插件。

apply plugin: 'com.android.library'
apply plugin: 'realm-android'

在项目级别上,我正在设置正确的依赖项:

dependencies {
    classpath 'com.android.tools.build:gradle:2.1.0'
    classpath "io.realm:realm-gradle-plugin:0.90.1"
    // NOTE: Do not place your application dependencies here; they belong
    // in the individual module build.gradle files
}

我没有想法。如果我尝试访问任何内容,我会按预期检索 GroupRealm,但通过代理类公开的所有公共属性都返回 null!

【问题讨论】:

  • 您不能信任来自调试器的输出错误。我们没有将数据存储在字段中,而是使用 getter/setter 从底层数据库中获取它们。此处对此进行了描述:realm.io/docs/java/latest/#debugging
  • 我只是将私有字段与 getter 和 setter 一起使用。当我这样做时,一切正常,不得不制作所有这些样板代码只是烦人。
  • 如果没有正确应用转换器,这是一个巨大的错误。请尽可能详细地在github.com/realm/realm-java/issues 上创建问题。然后我们就可以深入了解它了。
  • 遇到了这个问题,花了1个小时才找到答案............在这里。

标签: android realm


【解决方案1】:

文档中的相关常见问题解答:https://realm.io/docs/java/latest/#debugging


Realm 使用 Android Gradle Transform API。它提供了在将编译的类文件转换为 dex 文件之前对其进行操作的可能性。
io.realm.transformer.RealmTransformerio.realm.transformer 中的更多细节。 BytecodeModifier 类,可以在领域的 github 中找到。

RealmTransformer 的作用包括:

  • 用适当的 Realm 访问器替换对用户 RealmObjects 字段的所有访问。

您还可以查看文件夹 app/build/intermediates/transforms/RealmTransformer/

中的结果类

setter 示例:
您的代码行:

gr.imageUrl = g.GlyphUrl;

将被替换为:

String var5 = g.GlyphUrl;
gr.realmSet$imageUrl(var5);

getter 示例:

String url = gr.imageUrl;

将被替换为:

String url = gr.realmGet$imageUrl();

示例用例

  1. 您已创建类 GroupRealm。 Realm 使用 Transform API 生成 GroupRealmRealmProxy。这个代理类看起来像这样:

    public class GroupRealmRealmProxy extends GroupRealm implements RealmObjectProxy, GroupRealmRealmProxyInterface {
        private final GroupRealmRealmProxy.GroupRealmColumnInfo columnInfo;
        private final ProxyState proxyState;
        private RealmList<GroupRealm> childrenRealmList;
        private RealmList<ContentRealm> contentsRealmList;
        private static final List<String> FIELD_NAMES;
    
        GroupRealmRealmProxy(ColumnInfo columnInfo) {
            ...
        }
    
        public String realmGet$id() {
            this.proxyState.getRealm$realm().checkIfValid();
            return this.proxyState.getRow$realm().getString(this.columnInfo.idIndex);
        }
    
        public void realmSet$id(String value) {
            this.proxyState.getRealm$realm().checkIfValid();
            if(value == null) {
                this.proxyState.getRow$realm().setNull(this.columnInfo.idIndex);
            } else {
               this.proxyState.getRow$realm().setString(this.columnInfo.idIndex, value);
            }
        }
    
        public String realmGet$name() {
            this.proxyState.getRealm$realm().checkIfValid();
            return this.proxyState.getRow$realm().getString(this.columnInfo.nameIndex);
        }
    
        public void realmSet$name(String value) {
            this.proxyState.getRealm$realm().checkIfValid();
            if(value == null) {
                this.proxyState.getRow$realm().setNull(this.columnInfo.nameIndex);
            } else {
                this.proxyState.getRow$realm().setString(this.columnInfo.nameIndex, value);
            }
         }
    
        ...
    }
    

    您可以观察到方法 realmSet$namerealmGet$name 无法访问在 GroupRealm 类中声明的字段 name。他们使用 proxyState

  2. 现在,让我们回到 GroupRealm 的用法。调试代码时:

    GroupRealm gr = db.where(GroupRealm.class).equalTo("id",g.GroupID).findFirst();
    if(gr==null){
        gr = db.createObject(GroupRealm.class,g.GroupID);
    }
    gr.imageUrl = g.GlyphUrl;
    gr.name = g.Title;
    gr.order = g.OrderNum;
    

    实际上它的反编译版本如下所示:

    GroupRealm gr = (GroupRealm)realm.where(GroupRealm.class).equalTo("id", g.GroupId).findFirst();
    if(gr == null) {
        gr = (GroupRealm)realm.createObject(GroupRealm.class, g.GroupId);
    }
    
    String var7 = g.GlyphUrl;
    gr.realmSet$imageUrl(var7);
    var7 = g.Title;
    gr.realmSet$name(var7);
    int var8 = g.OrderNum;
    gr.realmSet$order(var8);
    

    首先,grGroupRealmRealmProxy类的实例。如您所见,gr.name 的设置被 gr.realmSet$name(var7) 取代。这意味着 GroupRealm 的字段 name 从未使用过。 realmGet$ 的情况与此类似。

在调试时,您会看到您的源代码版本,但实际上您使用的是带有注入方法 realmSet$realmGet$ 的修改版本。

【讨论】:

  • 那么只有二传手被替换了吗?
  • setter 和 getter 都在字节码级别被替换。事实上,每个使用来自任何 RealmObject 的任何字段(使用 @Ignore 注释的字段除外)的方法都会被修改。对于 RealmObject 类中的每个字段,都会生成方法 realmGet${fieldName}realmSet${fieldName}。每当您的代码的任何部分尝试获取或设置 RealmObject's 字段时,都会使用这些方法。
  • 我的意思是我的问题。我可以设置值但从不检索它们的事实使我相信只有设置器在工作。
  • 我已经用用例更新了我的答案。如果有帮助,请告诉我。
  • @Theyouthis 我也有同样的问题,如果它为空,你如何获取数据?
【解决方案2】:

字段为空。您可以通过替换所有字段访问的本机方法访问属性。以前(在 0.88.0 之前)它用于创建一个动态代理来覆盖您的 getter 和 setter 以使用它们的本机代理实现。

这些字段没有值。但正如您所看到的,Realm 对象的值很好:它在 toString() 值中如此说明。

【讨论】:

    【解决方案3】:

    对此没有什么可做的。由于 Realm 正在做的“聪明”的事情,调试器完全不能做它应该做的事情。您将不得不依赖大量的 Log.d 语句。

    对不起。这就是它的现实。

    【讨论】:

      【解决方案4】:

      如果您直接使用从您的 Realm 中检索到的 RealmObject,上述答案都可以。使用 Managed RealmObject(对象“直接”与您的 Realm 连接,因此您的 Realm 内的对象的“真实实例”,您只能在 RealmTransaction 内部修改,并且哪些更改会立即影响所有其他 Managed RealmInstance)您看不到它们的值由于代理,在调试器内部。

      无论如何,您可以通过使用 NO MANAGED 对象来解决此问题,因此通过从领域复制 RealmObject:

      MyRealmObject obj = getRealmObjectFromRealm();
      if(obj != null){
          obj = mRealm.copyFromRealm(obj);
      }
      

      这样您将在调试器中看到领域对象的所有属性。 显然,如果您需要在代码中使用托管领域对象,则在调试时需要通过创建另一个“MyRealmObject”实例来更改代码,该实例是另一个“MyRealmObject”领域的副本。

      这样您将在调试器中看到所有对象属性 (:

      希望这有帮助, 问候并有一个很好的编码!

      :D

      【讨论】:

        【解决方案5】:

        这是因为 Realm 代理模型是零副本存储。

        可以使用 Kotlin Realm 扩展,Vicpnm 库https://github.com/vicpinm/Kotlin-Realm-Extensions

        如果您仍然想在 Java 中使用,那么您可以通过以下方式实现它:-

        Realm.getDefaultInstance().copyFromRealm(realmObject)
        

        【讨论】:

          猜你喜欢
          • 2015-08-09
          • 2014-05-25
          • 1970-01-01
          • 1970-01-01
          • 2020-11-29
          • 2015-06-08
          • 1970-01-01
          • 1970-01-01
          • 2019-10-24
          相关资源
          最近更新 更多