Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
LiveRealmData.kt
/**
* Class connecting the Realm lifecycle to that of LiveData objects.
* Realm will remain open for as long as any LiveData objects are being observed.
*/
abstract class LiveRealmData<T: RealmModel>(val config: RealmConfiguration) : LiveData<RealmResults<T>>() {
private val listener = RealmChangeListener<RealmResults<T>> { results -> value = results }
private lateinit var realm: Realm
private var results: RealmResults<T>? = null
override final fun onActive() {
realm = Realm.getInstance(config)
results = runQuery(realm);
results.addChangeListener(listener)
value = results;
}
override final fun onInactive() {
results!!.removeAllChangeListeners()
results = null
realm.close()
}
abstract fun runQuery(realm: Realm): RealmResults<T>
}
fun usage() : LiveData<RealmResults<Person>> {
return object: LiveRealmData<Person>(getConfig()) {
override fun runQuery(realm: Realm): RealmResults<Person> {
// Called on UI thread
return realm.where(Person::class.java).findAllAsync()
}
}
}
@ericmaxwell2003

This comment has been minimized.

Copy link

commented May 25, 2017

Translated to Java I have

/**
 * Class connecting the Realm lifecycle to that of LiveData objects.
 * Realm will remain open for as long as any LiveData objects are being observed.
 */
public abstract class LiveRealmData<T extends RealmModel> extends LiveData<RealmResults<T>> {

    private Realm realm;
    private RealmResults<T> results;
    private final RealmChangeListener<RealmResults<T>> listener =
            new RealmChangeListener<RealmResults<T>>() {
                @Override
                public void onChange(RealmResults<T> results) {
                    setValue(results);
                }
            };


    @Override
    protected void onActive() {
        realm = Realm.getDefaultInstance();
        results = runQuery(realm);
        results.addChangeListener(listener);
    }

    @Override
    protected void onInactive() {
        results.removeChangeListener(listener);
        results = null;
        realm.close();
        realm = null;
    }

    public abstract RealmResults<T> runQuery(Realm realm);
}

and usage

public LiveRealmData<Loan> findLoansByNameAfter(final String userName, final Date after) {
        return new LiveRealmData<Loan>() {
            @Override
            public RealmResults<Loan> runQuery(Realm realm) {
                return realm.where(Loan.class)
                        .like("user.name", userName)
                        .greaterThan("endTime", after)
                        .findAllAsync();
            }
        };
    }

From ViewModel

  LiveRealmData<Loan> loans;

  public ViewModelConstructor(Application app) {
       loans = loanModel(mDb).findLoansByNameAfter("Mike", getYesterdayDate());
  }

Does that look about right?

@cmelchior

This comment has been minimized.

Copy link
Owner Author

commented May 25, 2017

Yup, that looks correct.

@ericmaxwell2003

This comment has been minimized.

Copy link

commented May 25, 2017

👍

@ericmaxwell2003

This comment has been minimized.

Copy link

commented May 25, 2017

I think this might work better.

public class LiveRealmData<T extends RealmModel> extends LiveData<RealmResults<T>> {

    private RealmResults<T> results;
    private final RealmChangeListener<RealmResults<T>> listener = new RealmChangeListener<RealmResults<T>>() {
        @Override
        public void onChange(RealmResults<T> results) { setValue(results);}
    };

    public LiveRealmData(RealmResults realmResults) {
        results = realmResults;
    }

    @Override
    protected void onActive() {
        results.addChangeListener(listener);
    }

    @Override
    protected void onInactive() {
        results.removeChangeListener(listener);
    }

}

With the caller passing the realm instance in because the onActive onInactive fires on rotation. I think we don't want to re-run the query and close/open Realm each time. Just start/stop observing data changes.

    public LiveRealmData<Loan> findLoansByNameAfter(final String userName, final Date after) {
        return asLiveData(mRealm.where(Loan.class)
                .like("user.name", userName)
                .greaterThan("endTime", after)
                .findAllAsync());
    }

And Kotlin for the glue, so that it's available directly on RealmResults from kotlin.

fun <T:RealmModel> RealmResults<T>.asLiveData() = LiveRealmData<T>(this)
@cmelchior

This comment has been minimized.

Copy link
Owner Author

commented May 26, 2017

Yes, if you only use async queries, that will work as well. The ViewModel becomes responsible for the Realm lifecycle, but neither looks wrong to me. If the ViewModel uses the Realm for other things (which is probably likely), then your approach looks cleaner.

You can then argue if you should call setValue() with the unloaded RealmResults in the constructor or not. Bot have their use cases I guess.

@suryachintu

This comment has been minimized.

Copy link

commented Jul 11, 2017

public LiveRealmData(RealmResults realmResults) {
results = realmResults;
setValue(results);
}
setValue() should be called in the constructor.

@kuno

This comment has been minimized.

Copy link

commented Jul 12, 2017

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.