Search code examples
rx-javareactive-programmingrx-java2

RxJava: protect an existing API method from excessive async calls by users by `debouncing()`


Let's say I have an instance singleton class with a very popular method paint():

public MyClass {
    public void paint() {
        // repaint the screen
    };
}

Code from all over the place calls this paint() method at unspecified times asynchronously. E.g. there's a timer that periodically calls that method, every time a user clicks a mouse button in an app it gets called, etc. I want to convert this direct call of paint() by user code to something that produces an Observable that I could debounce(). Something like:

public MyClass {
    Observable<PaintRequest> obs;

    private init() {
        obs = (???).debounce(N ms).subscribe(e -> {
            actuallyPaint();
        });
    }
    public void paint() {
        // push an event into an Observable somehow?
    }
    private void actuallyPaint() {...}
}

Can't wrap my head around the design.


Solution

  • PublishRelay is quite a good candidate to achieve the requested functionality. Relay is both observer and provider, so you can listen to values and push values via the accept() method. Method hide() hides relay implementation from a consumer, and he sees only plain Observable.

    class MyClass {
    
        private final PublishRelay<Integer> paintedRelay = PublishRelay.create();
    
        public void paint() {
            // paint to canvas
    
            // notify observers
            paintedRelay.accept(0);
        }
    
        public Observable<Integer> getPaintedNotifier() {
            return paintedRelay.hide();
        }
    }
    
    public class SO64938709Test {
    
        @Test
        public void whenPaintedThenNotified() {
            MyClass tested = new MyClass();
    
            TestObserver<Integer> testObserver = tested.getPaintedNotifier().test();
    
            tested.paint();
    
            testObserver.assertValueCount(1);
        }
    }