0

I want to map my realm results to an immutable viewmodel, and I want to listen to results changes, so i'm emitting them PublishSubject, However, the data doesn't appear in my recyclerview, until I rotate the device, this issue is fixed when I remove observeOn(AndroidSchedulers.mainThread()).


Repository:

fun notionsChanges(state: Boolean): Observable<Pair<MutableList<Notion>, OrderedCollectionChangeSet?>> {

        val notionsChanges = PublishSubject.create<Pair<MutableList<Notion>, OrderedCollectionChangeSet?>>()

        val realm = Realm.getDefaultInstance()
        val queryResult = realm.where<Notion>()
                .equalTo("isArchived", state)
                .findAllAsync()
        val listener: OrderedRealmCollectionChangeListener<RealmResults<Notion>> = OrderedRealmCollectionChangeListener { realmResults, changeSet ->
            if (realmResults.isValid && realmResults.isLoaded) {
                val results: MutableList<Notion> = realm.copyFromRealm(realmResults)
                notionsChanges.onNext(results to changeSet)
            }
        }
        queryResult.addChangeListener(listener)
        notionsChanges.doFinally {
            queryResult.removeChangeListener(listener)
            closeRealm(realm)
        }.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())

        return notionsChanges
}

in my presenter I use this observable to map the model to a view model, then i show(when subscribe) the data in recyclerview inside a fragment:

private var subscriptions: CompositeDisposable = CompositeDisposable()

override fun onResume() {
    super.onResume()
    showData()
}

override fun onPause() {
    subscriptions.clear()
    super.onPause()
}

private fun showData() {
        val viewModel = present(idleStates, resources, isIdle)
        with(viewModel) {
            subscriptions.addAll(
                    notionsChanges.subscribe(notionsAdapter::handleChanges),
                    //other subscriptions.
            )
        }
}

notionsAdapter.handleChanges:

fun handleChanges(collectionChange: Pair<List<NotionCompactViewModel>, OrderedCollectionChangeSet?>) {
    val (collection, changeset) = collectionChange
    debug("${collection.size}") //correctly prints the actual size of the collection.
    replaceAll(collection)
    if (changeset == null)
        notifyDataSetChanged()
    else {
        for (change in changeset.changeRanges)
            notifyItemRangeChanged(change.startIndex, change.length)

        for (insertion in changeset.insertionRanges)
            notifyItemRangeInserted(insertion.startIndex, insertion.length)

        for (deletion in changeset.deletionRanges)
            notifyItemRangeRemoved(deletion.startIndex, deletion.length)
    }
}

sorry if the code is unclear.


edit: my onBindViewHolder doesn't get called sometimes(when recyclerview is empty, of course).

mhashim6
  • 527
  • 6
  • 19
  • The real surprise is that this works at all. `findAllAsync` and `addChangeListener` aren't supported on non-looper threads, this should be running on a scheduler created from a HandlerThread's Looper. – EpicPandaForce Sep 05 '18 at 21:42
  • even though I'm calling this from the main thread? – mhashim6 Sep 05 '18 at 21:43
  • Ah, your `subscribeOn(io())` tricked me. Then this is just unsafe for large data sets. Whoops! Are you sure you shouldn't use a BehaviorSubject instead? – EpicPandaForce Sep 05 '18 at 21:46
  • what I was trying to achieve is to retrieve the results on the main thread, then operate on them in the background then display them in the main thread. – mhashim6 Sep 05 '18 at 21:46
  • I tried BehaviorSubject, but it showed the same behavior. – mhashim6 Sep 05 '18 at 21:47
  • I did this because when I tried [this solution](https://stackoverflow.com/a/43202425/3578585) it did the same issue. – mhashim6 Sep 05 '18 at 21:49
  • 1
    Hold on a second. Are you using Realm 5.0+? Changeset is no longer nullable. You get `state == INITIAL`. – EpicPandaForce Sep 05 '18 at 22:13
  • yes, realm v5.4.2. – mhashim6 Sep 05 '18 at 22:13
  • Once again, You've answered my question, please post this in an answer, thanks! Also, If you don't mind, is this approach a good way to achieve what I've been trying to do? – mhashim6 Sep 05 '18 at 22:19
  • Technically it makes sense, but using `copyFromRealm()` on UI thread can cause ANR with large dataset, which is why I recommended the HandlerThread on that other answer you linked. – EpicPandaForce Sep 06 '18 at 08:43
  • 1
    Yeah that's what I'm using right now, now that original problem(with changeSet) is solved, your other solution with clean architecture is working, too, thanks again. – mhashim6 Sep 06 '18 at 15:01

1 Answers1

0

Since Realm 5.0, the initial changeset is no longer signaled with changeset == null.

You need to check:

if(changeSet.getState() == State.INITIAL) {
    adapter.notifyDataSetChanged() 
EpicPandaForce
  • 79,669
  • 27
  • 256
  • 428