1

I'm just starting using RxJava and I'm struggling to figure out how to use observables the right way.

Down below I have a pager and I'd like to call a web service on page scroll avoiding to many useless calls.

So I found that 'debounce' operator is what I am looking for but in my case it's not working and the web service is called everytime..

v.pager.addOnScrollListener(object : RecyclerView.OnScrollListener() {

    override fun onScrollStateChanged(recyclerView: RecyclerView?, newState: Int) {
        super.onScrollStateChanged(recyclerView, newState)
        if (newState == RecyclerView.SCROLL_STATE_IDLE) {

            val itemPosition : Int = layoutManager.findFirstCompletelyVisibleItemPosition();

            Observable.just(itemPosition).debounce(1500, TimeUnit.MILLISECONDS).map {
                  retrieveUserDetail(userList[itemPosition])
            }.observeOn(AndroidSchedulers.mainThread()).subscribe()

         }
    }
})

What's wrong with this code?

Phantômaxx
  • 37,901
  • 21
  • 84
  • 115
Stack Diego
  • 1,309
  • 17
  • 43
  • 1
    Declare your `Observable` outside the scope of the listener callback. – PPartisan Dec 06 '18 at 14:29
  • I tried declaring it outside of the listener and just subscribe inside it but without success.. – Stack Diego Dec 06 '18 at 14:31
  • Debounce adds a delay to the Observable emitting items, it does not prevent the subscription. You're also creating a new observable out of itemPosition every time and subscribing to it immediately after. – Mauro Curbelo Dec 06 '18 at 15:11
  • @MauroCurbelo that makes sense, so how should I edit the code? – Stack Diego Dec 06 '18 at 15:17
  • That's a good question, and I'd never had to deal with this situation so I'm not sure if there is a RxJava specific way. First, declare your observable outside of the scope so it's not a new observable every time. Second, a kind of hacky solution would be to keep a boolean and prevent the subscription until the previous one has completely finished. – Mauro Curbelo Dec 06 '18 at 15:43
  • 1
    I don't know my way around Kotlin that well, but the process should be: (1) you define an emitter of some sort, as an instance variable, and call `onNext()` inside your scrolllistener. The simplest would probably be a `Subject` (i.e. `PublishSubject`). Then (2), somewhere else, subscribe to your emitter with the rest of your chain, including debounce. – PPartisan Dec 06 '18 at 15:52

1 Answers1

2

As explained by Stack Diego and PPartisan in the comments, you should create the observable outside the listener and subscribe to it once. Since each observable only emits one item, debounce won't have any affect.

Your code would probably look something like this:

Declare your subject as a member variable.

val subject: PublishSubject<Int> = PublishSubject.create()

Subscribe to the subject outside the listener (you might want to do this in onResume or onStart if you plan on disposing the observable when the activity is paused)

subject
    .debounce(1500, TimeUnit.MILLISECONDS)
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe { retrieveUserDetail(userList[itemPosition]) }

And then inside your scroll listener, emit a new item when onScrollStateChanged() is called

v.pager.addOnScrollListener(object : RecyclerView.OnScrollListener() {

    override fun onScrollStateChanged(recyclerView: RecyclerView?, newState: Int) {
        super.onScrollStateChanged(recyclerView, newState)
        if (newState == RecyclerView.SCROLL_STATE_IDLE) {
            val itemPosition : Int = layoutManager.findFirstCompletelyVisibleItemPosition()
            subject.onNext(itemPosition)
         }
    }
})
Damon Baker
  • 887
  • 7
  • 9
  • And it just works ;) Basically from what I understand debounce works with the emissions of the same subscription, but I was creating a new subscription every time. onNext() did the trick, thanks – Stack Diego Dec 07 '18 at 07:21