【问题标题】:Query realm data contained on other object查询其他对象上包含的领域数据
【发布时间】:2016-07-06 09:08:00
【问题描述】:

这个问题是来自Organize Android Realm data in lists的后续问题

由于我们使用的 API 返回的数据,稍微不可能对领域数据库进行实际查询。相反,我将订购的数据包装在 RealmList 中,并向其中添加 @PrimaryKey public String id;

所以我们的领域数据看起来像:

public class ListPhoto extends RealmObject {
   @PrimaryKey public String id;
   public RealmList<Photo> list; // Photo contains String/int/boolean
}

只需将 API 端点用作id,就可以轻松地从 Realm DB 写入和读取。

所以一个典型的查询看起来像:

realm.where(ListPhoto.class).equalTo("id", id).findFirstAsync();

这会对数据产生少量的listening/subscribing 开销,因为现在我需要检查listUser.isLoaded() 使用ListUseraddChangeListener/removeChangeListenerListUser.list 作为我适配器上的实际数据。

所以我的问题是:

有没有办法可以查询此领域以接收RealmResults&lt;Photo&gt;。这样我就可以轻松地在RealmRecyclerViewAdapter 中使用这些数据并直接在其上使用监听器。

编辑:为了进一步澄清,我想要类似以下的内容(我知道这不会编译,它只是我想要实现的伪代码)。

realm
 .where(ListPhoto.class)
      .equalTo("id", id)
      .findFirstAsync()  // get a results of that photo list
 .where(Photo.class)
      .getField("list")
      .findAllAsync(); // get the field "list" into a `RealmResults<Photo>`

编辑最终代码:考虑到 ATM 无法直接在查询中执行此操作,我的最终解决方案是简单地使用一个适配器来检查数据并在需要时进行订阅。代码如下:

public abstract class RealmAdapter
                     <T extends RealmModel, 
                      VH extends RecyclerView.ViewHolder> 
            extends RealmRecyclerViewAdapter<T, VH>
            implements RealmChangeListener<RealmModel> {

   public RealmAdapter(Context context, OrderedRealmCollection data, RealmObject realmObject) {
      super(context, data, true);
      if (data == null) {
         realmObject.addChangeListener(this);
      }
   }

   @Override public void onChange(RealmModel element) {

      RealmList list = null;
      try {
         // accessing the `getter` from the generated class
         // because it can be list of Photo, User, Album, Comment, etc
         // but the field name will always be `list` so the generated will always be realmGet$list
         list = (RealmList) element.getClass().getMethod("realmGet$list").invoke(element);
      } catch (Exception e) {
         e.printStackTrace();
      }

      if (list != null) {
         ((RealmObject) element).removeChangeListener(this);
         updateData(list);
      }
   }
}

【问题讨论】:

  • 有什么方法可以查询这个领域你到底想查询什么? realm.where(ListPhoto.class).equalTo("id", id).findFirstAsync();的结果?
  • @TimCastelijns 是的。我想要一个与ListPhoto 中的RealmList&lt;Photo&gt; 匹配的RealmResults&lt;Photo&gt;(甚至直接是RealmList)。我知道我可以通过同步调用来做到这一点。但我真的更喜欢使用异步。
  • 我用一些伪代码编辑了这个问题。我希望它更清楚
  • 我对最终的答案不是很满意,所以我需要问:一张照片可以属于多个ListPhoto吗?
  • @EpicPandaForce(史诗般的昵称),我刚刚意识到我无法以我访问的方式访问该字段。并将其删除。我仍在检查我能做什么,但它应该与你以前看到的没有太大不同。但要回答你的问题:是的。一张照片只属于一个用户,但它可以在多个相册中,在热门照片列表中,如果orderBydatetop,它可以在同一个相册中显示两次

标签: android realm realm-list


【解决方案1】:

首先您查询 ListPhoto,因为它是异步的,您必须为结果注册一个侦听器。然后在该侦听器中,您可以查询结果以获取 RealmResult。

类似的东西

final ListPhoto listPhoto = realm.where(ListPhoto.class).equalTo("id", id).findFirstAsync();
listPhoto.addChangeListener(new RealmChangeListener<RealmModel>() {
    @Override
    public void onChange(RealmModel element) {
        RealmResults<Photo> photos = listPhoto.getList().where().findAll();
        // do stuff with your photo results here.


        // unregister the listener.
        listPhoto.removeChangeListeners();
    }
});

请注意,您实际上可以查询 RealmList。这就是为什么我们可以打电话给listPhoto.getList().where()where() 只是表示“全部归还”。

我无法测试它,因为我没有您的代码。您可能需要将element 转换为((ListPhoto) element)

【讨论】:

  • 感谢蒂姆。我害怕这是唯一的答案。像这样我无法在onCreate 期间直接生成 RealmResult(或列表),必须等待回调。
  • @Budius 如果您使用异步查询,是的,那么您必须等待回调:-)
  • @Budius 我原以为你会在活动中或任何地方进行查询,并将RealmResults&lt;Photo&gt; photos = listPhoto.getList().where().findAll(); 的结果传递给适配器而不是在适配器内部进行查询
  • 这是一个稍微复杂的系统,但其想法是适配器是在onCreate 期间创建的。所以我将传递给构造函数data.isLoaded()?data.getList():null,如果列表为空,我会听它的变化。目前,我正在为我们创建的这个 (github.com/eyeem/decorator) 库编写新的示例代码,同时检查将其替换到我们的应用程序中的可行性。
【解决方案2】:

我知道你说你没有考虑使用同步 API 的选项,但我仍然认为值得注意的是,你的问题会这样解决:

RealmResults<Photo> results = realm.where(ListPhoto.class).equalTo("id", id).findFirst()
                           .getList().where().findAll();

编辑:不过,为了提供完整的信息,我引用docs

findFirstAsync

public E findFirstAsync()

与 findFirst() 类似,但在工作线程上异步运行此方法仅在 Looper 线程中可用。

返回:立即是一个空的 RealmObject。

在加载之前尝试访问返回对象上的任何字段 将抛出 IllegalStateException。

使用 RealmObject.isLoaded() 检查对象是否已完全加载

或注册一个监听器RealmObject.addChangeListener(io.realm.RealmChangeListener&lt;E&gt;) 查询完成时通知。

如果之后没有找到 RealmObject 查询完成,返回的 RealmObject 将有 RealmObject.isLoaded() 设置为 true 并且 RealmObject.isValid() 设置为 假的。

所以从技术上讲是的,您需要执行以下操作:

private OrderedRealmCollection<Photo> photos = null;
//...

final ListPhoto listPhoto = realm.where(ListPhoto.class).equalTo("id", id).findFirstAsync();
listPhoto.addChangeListener(new RealmChangeListener<ListPhoto>() {
    @Override
    public void onChange(ListPhoto element) {
        if(element.isValid()) {
            realmRecyclerViewAdapter.updateData(element.list);
        }
        listPhoto.removeChangeListeners();
    }
}

【讨论】:

  • 你能解释一下这与直接使用这样的列表有何不同:realm.where(ListPhoto.class).equalTo("id", id).findFirst().getList() 我的意思是,如果列表已经存在,我不需要第二次查询,我可以直接使用RealmList&lt;Photo&gt; 对象。没有?
  • 从技术上讲是的,因为我认为 0.89.0 和 OrderedRealmCollection&lt;T&gt; 接口的引入,但你说你想要一个 RealmResults&lt;Photo&gt;,这会将列表转换为结果。但这只会使您的list 被视为view,实际上进行.where().findAll() 转换并不需要任何费用。
  • 我想要RealmResult,因为我的想法是能够从查询中异步获取整个内容。如果是同步的,我就直接用RealmList。非常感谢您的答案和cmets。我需要一些时间来平衡这些方法之间的利弊,但它非常有见地。我希望我能将两者都标记为正确。
  • 我明白了。好吧,我个人更信任同步 API,因为异步查询背后的机制非常复杂——而且由于查询的惰性(只有你查询的元素才是被评估的),同步 API 也不慢。尽管我倾向于建议,如果您按字段查询,则应始终将@Index 放在上面 - 但@PrimaryKeys 也是@Index
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-11-20
  • 1970-01-01
相关资源
最近更新 更多