合并多个RealmList并对结果列表进行排序? [英] Merge multiple RealmList´s and sort resulting List?

查看:76
本文介绍了合并多个RealmList并对结果列表进行排序?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我刚开始在当前的android应用程序中使用Realm,到目前为止,它很棒. 不幸的是,我遇到了一个问题:

I just started using Realm in my current android app and so far it is great. Unfortunately I´ve encountered a problem:

在我的应用中,用户可以在日记中添加不同类型的条目(他今天吃了什么?他喝了什么饮料?等等). 一个DiaryEntry对象代表给定日期(例如,2017年5月21日等)所有条目的总和.

In my app the user can add different kind of entries (What did he eat this day? What drinks did he have?, etc.) into his diary. One DiaryEntry object represents the sum off all entries for a given day (e.g. 21.05.2017, etc.).

public class DiaryEntry extends RealmObject {

    // ID of the day this diary entry represents
    @PrimaryKey private Integer dateId;

    private RealmList<MealEntry> mealEntries;
    private RealmList<DrinkEntry> drinkEntries;
    private RealmList<SymptomEntry> symptomEntries;
    private RealmList<MedicineEntry> medicineEntries;

    public void addMealEntry(MealEntry mealEntry) {
        mealEntries.add(mealEntry);
    }
    public RealmList<MealEntry> getMealEntries() {
        return mealEntries;
    }

    public void addDrinkEntry(DrinkEntry drinkEntry) {
        drinkEntries.add(drinkEntry);
    }
    public RealmList<DrinkEntry> getDrinkEntries() {
        return drinkEntries;
    }

    public void addSymptomEntry(SymptomEntry symptomEntry) {
        symptomEntries.add(symptomEntry);
    }
    public RealmList<SymptomEntry> getSymptomEntries() {
        return symptomEntries;
    }

    public void addMedicineEntry(MedicineEntry medicineEntry) {
        medicineEntries.add(medicineEntry);
    }
    public RealmList<MedicineEntry> getMedicineEntries() {
        return medicineEntries;
    }
}

要在日记中显示特定日期的数据,应按用户创建它们的时间对所有条目进行排序. 因此,每个输入对象都包含一个时间"字段.

To display this data for a particular day in the diary, all entries should be sorted by the time the user created them. So every entry object contains a field 'time'.

private int time;

我为我的问题想出了一个临时解决方案,但这远非完美. 下面的代码是在UI线程上执行的,这显然是不好的做法.

I came up with a temporary solution for my problem, but it´s far from perfect. The follwing code is executed on the UI Thread, which obviously is bad practice.

List<RealmObject> entryList = new ArrayList<>();
        OrderedRealmCollectionSnapshot<MealEntry> mealEntries = diaryEntry.getMealEntries().createSnapshot();
        OrderedRealmCollectionSnapshot<DrinkEntry> drinkEntries = diaryEntry.getDrinkEntries().createSnapshot();
        OrderedRealmCollectionSnapshot<MedicineEntry> medicineEntries = diaryEntry.getMedicineEntries().createSnapshot();
        OrderedRealmCollectionSnapshot<SymptomEntry> symptomEntries = diaryEntry.getSymptomEntries().createSnapshot();

        entryList.addAll(mealEntries);
        entryList.addAll(drinkEntries);
        entryList.addAll(medicineEntries);
        entryList.addAll(symptomEntries);

        Collections.sort(entryList, entryComparator);

对条目列表进行排序的代码通过对time字段调用getter方法来使用反射:

The code to sort the entry list uses reflection by invoking the getter method for the time field:

public int compare(RealmObject entry1, RealmObject entry2) {
            try {
                Method timeGetter1 = entry1.getClass().getMethod("getTime");
                Method timeGetter2 = entry2.getClass().getMethod("getTime");

                int time1 = (Integer) timeGetter1.invoke(entry1);
                int time2 = (Integer) timeGetter2.invoke(entry2);

                return time1 - time2;
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
                Timber.d("No such method 'getTime'.");
           }
      // Other catch clauses

正如我之前所说,所有这些都发生在UI线程上.

As I said earlier, all of this happens on the UI thread.

我知道我不能跨线程传递RealmObject s,RealmList s和RealmResult s,所以我真的很难为此提供一个异步解决方案.我想到启动一个后台线程,并在其中创建DiaryEntry对象中所有RealmList的副本.然后合并此非托管列表并对其进行排序-所有这些都在后台线程上.

I know that I can´t pass RealmObjects, RealmLists and RealmResults across threads so I really have a hard time coming up with an async solution for that. I thought of starting a background thread and in there create copies of all the RealmList´s inside a DiaryEntry object. Then merge this unmanaged lists and sort it - all of that on the background thread.

所以我的问题是:是否有用于合并多个RealmList并对合并列表进行排序的首选策略-所有这些都是异步方式?我上面描述的尝试会成功吗?

So my question: Are there any preferred strategies for merging multiple RealmLists and sorting the merged list - all of that in an async fashion? Would my attempt I described above work?

推荐答案

感谢@EpicPandaForce

Thanks @EpicPandaForce

我完全按照您描述它的方式解决了它,它就像一个魅力一样-现在,我甚至具有实时功能,无需手动刷新数据,尼斯:)

I solved it exactly the way you described it and it works like a charm - now I even have the real-time functionality and no need to refresh the data manually, Nice :)

万一有人遇到相同的问题,我会在此处发布一些代码片段,以展示如何用代码解决它.

In case anybody faces the same problem I post some code pieces here that show how I solved it in code.

public class Entry extends RealmObject {
    private static final int ENTRY_MEAL = 0;
    private static final int ENTRY_DRINK = 1;
    private static final int ENTRY_SYMPTOM = 2;
    private static final int ENTRY_MEDICINE = 3;

    /** The tag describes what kind of entry it represents */
    private int tag;

    /* Only one of these can be set, according to what this entry represents. */
    @Nullable private MealEntry mealEntry;
    @Nullable private DrinkEntry drinkEntry;
    @Nullable private SymptomEntry symptomEntry;
    @Nullable private MedicineEntry medicineEntry;

    /** The time value this entry was created at */
    /** Format: hours + minutes * 60 */
    private int time;

    public int getTime() {
        return time;
    }

/* Can only be accessed from within the 'data' package */

    void setTime(int time) {
        this.time = time;
    }

/**
     * Creates a new entry object in the realm database and tags it as 'MEAL'
     *
     * @param realm not null
     * @param mealEntry the {@link MealEntry} object to map this entry to, not null
     *
     * @return the newly created entry
     */
    static Entry createEntryAsMeal(@NonNull final Realm realm, @NonNull final MealEntry mealEntry) {
        if(realm == null) {
            throw new IllegalArgumentException("'realm' may not be null");
        }
        if(mealEntry == null) {
            throw new IllegalArgumentException("'mealEntry' may not be null");
        }

        Entry entry = realm.createObject(Entry.class);
        entry.tag = ENTRY_MEAL;
        entry.mealEntry = mealEntry;
        return entry;
        }

/* Same methods for other tag types ... */

在MealEntry.class中:

In MealEntry.class:

public class MealEntry extends RealmObject {

    @PrimaryKey @Required private String id;

    @Required private String title;

    /** The entry objects this meal-entry is added to */
    Entry entry;

    /** This time value describes when the user consumed this meal **/
    private int time;
// other fields

/**
* Creates a new MealEntry object in the realm.
 * <p>
 *     Note: It is important to use this factory method for creating {@link MealEntry} objects in realm.
 *     Under the hood, a {@link Entry} object is created for every MealEntry and linked to it.
 * </p>
 *
 * @param realm not null
 *
 * @return new MealEntry object which has been added to the <code>realm</code>
 */
public static MealEntry createInRealm(@NonNull Realm realm) {
    if(realm == null) {
        throw new IllegalArgumentException("'realm' may not be null");
    }

    MealEntry mealEntry = realm.createObject(MealEntry.class, UUID.randomUUID().toString());
    mealEntry.entry = Entry.createEntryAsMeal(realm, mealEntry);
    return mealEntry;
}

Entry.class和MealEntry.class中存在时间"字段,因此,如果后者更改,则必须相应地更新Entry:

The 'time' field exists in the Entry.class and MealEntry.class so if the latter one changes the Entry must be updated accordingly:

/**
     * Sets the time value for the <code>mealEntry</code> to the specified value.
     * <p>
     *     Note: This method is necessary in order to sync the new time value with the underlying
     *     {@link Entry} object that is connected with the <code>mealEntry</code>.
     * </p>
     *
     * @param mealEntry the {@link MealEntry} object to set the time for, not null
     *
     * @param time the new time value, must be in range of [0, 24*60] because of the format: hours*60 + minutes
     *
     */
    public static void setTimeForMealEntry(@NonNull MealEntry mealEntry, @IntRange(from=0, to=24*60) int time) {
        if(mealEntry == null) {
            throw new IllegalArgumentException("'mealEntry' may not be null");
        }

        mealEntry.setTime(time);

        Entry entry = mealEntry.entry;
        if(entry == null) {
            throw new IllegalStateException("'mealEntry' contains no object of type 'Entry'! Something went wrong on creation of the 'mealEntry'");
        }

        /* Syncs the entries time value with the time value for this MealEntry. */
        /* That´s important for sorting a list of all entries. */
        entry.setTime(time);
    }

注意:我可以只将相应Entry对象的ID存储在MealEntry内,反之亦然,因为Entry对象将ID存储到相应的MealEntry对象.但是我不知道这在性能上有什么不同,所以我只是采用了上面的方法. 但是,采用另一种方法的一个原因是,我不必两次存储时间"字段,一次存储在Entry.class中,一次存储在MealEntry.class中,因为在Entry.class中,我可以获取时间通过根据其ID查找相应的MealEntry对象来获取值,然后获取时间.

Note: I could have stored only the ID of the corresponding Entry object inside the MealEntry and vice-versa for the Entry object store an ID to the corresponding MealEntry object. However I don´t know what a difference in perfomance this makes so I just went with the above approach. One reason for the other approach though would be that I wouldn´t have to store the 'time' field twice, once in the Entry.class and once in the MealEntry.class, because in the Entry.class I could just get the time value by finding the corresponding MealEntry object by its ID and then get the time.

这篇关于合并多个RealmList并对结果列表进行排序?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆