Search code examples
androidandroid-mediaplayerrx-javareactive-programmingrx-android

How to throttle/debounce seekbar seek events in MediaPlayer app


I'm writing a MediaPlayer Android application and wanted to throttle/debounce Seekbar events. Couldn't find a good solution to minimize the number of seek operations that is generated when a user moves his finger on the seekbar.

What is the simplest solution to do this without the use of additional libraries?

I'll post my solution which involves RxJava & RxBindings as answer to help others.


Solution

  • int SEEKBAR_THROTTLE_INTERVAL = 100; //ms
        mSeekBarSubscription = RxSeekBar.userChanges(mSeekBar)
                .doOnNext(new Action1<Integer>() {
                    @Override
                    public void call(Integer integer) {
                      mUpdateProgressRoutine.stopRoutine();
                    }
                })
                .debounce(SEEKBAR_THROTTLE_INTERVAL, TimeUnit.MILLISECONDS)
                .onBackpressureLatest()
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Action1<Integer>() {
                    @Override
                    public void call(Integer progress) {
                        onProgressChanged(mSeekBar, progress, true);
                    }
                });
    

    In this case I use debounce and do not throttle. I found the following analogy helpful:

    Imagine that you go home, enter in the elevator, doors are closing... and suddenly your neighbor appears in the hall and tries to jump on the elevator. Be polite! and open the doors for him: you are debouncing the elevator departure. Consider that the same situation can happen again with a third person, and so on... source

    The effect will be that if the user rapidly moves the finger over the seekbar only the last event will "win" and trigger a seek operation on the MediaPlayer instance: you won't hear "scratch-noise" or waste performance.

    Since I also couldn't find an example on how subscribe to ALL change events without using two different Observables I'll share an example:

    mSeekbarSubscription = RxSeekBar.changeEvents(mSeekBar)
                .ofType(SeekBarProgressChangeEvent.class)
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Action1<SeekBarProgressChangeEvent>() {
                    @Override
                    public void call(SeekBarProgressChangeEvent seekBarChangeEvent) {
                        SeekBar seekBar = seekBarChangeEvent.view();
                        int progress = seekBarChangeEvent.progress();
                        boolean fromUser = seekBarChangeEvent.fromUser();
                        //same as SeekbarListener.onProgressChanged
                    }
                });