Friday, 25 July 2014

RxJava for UI events on Android example

In the paper Deprecating the Observer Pattern, part 3 "Reactors: composable observers without inversion of control" there is an example of the reactor that processes mouse input events, written in Scala using scala-react library. In this post I'll describe how to create composable observers for touch events for Android with RxJava library.

The tools used are the RxJava library itself and retrolambda plugin to employ lambda functions on Android. Here is the sample build.gradle file with these two dependencies. To bridge from Android event listeners to RxJava Observers the Subject is created, it will listen to touch events and reemit them to RxJava Observers.
private final PublishSubject mTouchSubject = PublishSubject.create();
 
public MouseDragView(Context context, AttributeSet attr) {
        super(context, attr);
        
        setOnTouchListener((View v, MotionEvent event) -> {
            mTouchSubject.onNext(event);
                return true;
        });
}


Then the Observables are created by filtering the Subject Observable and each of them exposes corresponding event stream to touch ups, moves and touch downs. While each of these three new observables will receive all the events from the Subject Observable, they will call their subcribers' onNext() function only for the events that suit the filter predicate.

private final Observable mTouches = mTouchSubject.asObservable();
    private final Observable mDownObservable = 
               mTouches.filter(ev -> ev.getActionMasked() == MotionEvent.ACTION_DOWN);
    private final Observable mUpObservable =
               mTouches.filter(ev -> ev.getActionMasked() == MotionEvent.ACTION_UP);
    private final Observable mMovesObservable =
               mTouches.filter(ev -> ev.getActionMasked() == MotionEvent.ACTION_MOVE);


Path should be created with the touch down event. So the new Observer is spawned using the subcribe (final Action1 onNext) function of Observable. The lambda function that is passed as an argument will be the newly created Observer's onNext() handler.

    mDownObservable.subscribe(downEvent -> {
           final Path path = new Path();
           path.moveTo(downEvent.getX(), downEvent.getY());
           Log.i(downEvent.toString(), "Touch down");
    });


After the path is created and has the starting point, lets watch for the move events.

 mDownObservable.subscribe(downEvent -> {
            final Path path = new Path();
            path.moveTo(downEvent.getX(), downEvent.getY());
            Log.i(downEvent.toString(), "Touch down");

            mMovesObservable
                    .subscribe(motionEvent -> {
                        path.lineTo(motionEvent.getX(), motionEvent.getY());
                        draw(path);
                        Log.i(motionEvent.toString(), "Touch move");
            });
        });


For each touch down event the new Observer for move events is created. Now we need to also create an Observer for touch up event. On touch up we'll unsubscribe from move events, and close the path. It's important to unsubscribe because otherwise for each next touch down we will be creating yet another moves Observer while leaving behind all the moves Observers created for previous touch downs. The unsubscription is achieved via takeUntil() operator. The RxJava wiki states that takeUntil() "emits the items from the source Observable until another Observable emits an item or issues a notification. This means that our Observer will be automatically unsubscribed from moves Observable when touch up Observable emits an item. Hence the moves Observable will become "cold" Observable, as it doesn't have Observers, and will stop emitting the items.

    mDownObservable.subscribe(downEvent -> {
            final Path path = new Path();
            path.moveTo(downEvent.getX(), downEvent.getY());
            Log.i(downEvent.toString(), "Touch down");

            mMovesObservable
                     .takeUntil(mUpObservable
                             .doOnNext(upEvent -> {
                                 draw(path);
                                 path.close();
                                 Log.i(upEvent.toString(), "Touch up");
                             }))
                    .subscribe(motionEvent -> {
                        path.lineTo(motionEvent.getX(), motionEvent.getY());
                        draw(path);
                        Log.i(motionEvent.toString(), "Touch move");
                    });

        });


The full example project for Android Studio is at github



12 comments :

  1. What a nice article!

    I'd like to know if I can implement this example (with a few improvements) in an app I am working on. Basically it is an app which I expect to wrap 100+ examples of RxJava, including many operators, hot observables, backpressure, etc. I will publish it on GitHub.

    You can check my blog (androidahead.com) which I have already published some articles about RxJava. Basically this new app will include all examples already published plus some others I found useful (including this one).

    Of course all the credits for this example belong to you and I will add them on the article/code accordingly.

    So, could you please let me know your thoughts?

    Thanks!

    ReplyDelete
  2. This is best one article so far I have read online, I would like to appreciate you for making it very simple and easy
    Regards,
    Devops Training in Chennai | Devops Certification in Chennai

    ReplyDelete
  3. Thanks for such a great article here. I was searching for something like this for quite a long time and at last I’ve found it on your blog. It was definitely interesting for me to read  about their market situation nowadays.

    Microsoft Azure online training
    Selenium online training
    Java online training
    Java Script online training
    Share Point online training

    ReplyDelete
  4. This is such a good post. One of the best posts that I\'ve read in my whole life. I am so happy that you chose this day to give me this. Please, continue to give me such valuable posts. Cheers!
    devops online training

    aws online training

    data science with python online training

    data science online training

    rpa online training

    ReplyDelete
  5. Your info is really amazing with impressive content..Excellent blog with informative concept. Really I feel happy to see this useful blog, Thanks for sharing such a nice blog..
    If you are looking for any Data science Related information please visit our website data science institutes in bangalore page!

    ReplyDelete
  6. Your info is really amazing with impressive content..Excellent blog with informative concept. Really I feel happy to see this useful blog, Thanks for sharing such a nice blog..
    If you are looking for any Data science Related information please visit our website data science institutes in bangalore page!

    ReplyDelete