$30 off During Our Annual Pro Sale. View Details »

The Mayans Lost Guide to RxJava on Android

The Mayans Lost Guide to RxJava on Android

In this little journey, we are gonna see what RxJava offers to us in terms of android development. We will also learn how we can apply it to real life mobile projects by showing different examples and use cases.

Fernando Cejas

April 25, 2015
Tweet

More Decks by Fernando Cejas

Other Decks in Programming

Transcript

  1. @fernando_cejas
    http://www.fernandocejas.com
    The Mayans Lost Guide
    To RxJava on Android

    View Slide

  2. @fernando_cejas
    Curious learner
    Meet...
    Android lover
    Software engineer
    Geek

    View Slide

  3. View Slide

  4. #Reactive
    1- of, relating to, or marked by reaction or
    reactance.
    2- readily responsive to a stimulus.

    View Slide

  5. RxJava is a Java VM implementation
    of Reactive X (Reactive Extensions): ˝
    a library for composing asynchronous
    and event-based programs by using
    observable sequences.
    What is RxJava?

    View Slide

  6. Why should
    we go
    reactive?

    View Slide

  7. Multithreading is always complex˝
    Concurrency˝
    Java Futures are Expensive to Compose˝
    Java Futures˝
    Callbacks Have Their Own Problems˝
    Callbacks˝
    @benjchristensen from @netflix

    View Slide

  8. public class FuturesA {
    public static void run() throws Exception {
    ExecutorService executor =
    new ThreadPoolExecutor(4, 4, 1,
    TimeUnit.MINUTES,
    new LinkedBlockingQueue());
    Future f1 = executor.submit(new CallToRemoteServiceA());
    Future f2 = executor.submit(new CallToRemoteServiceB());
    System.out.println(f1.get() + " - " + f2.get());
    }
    }
    https://gist.github.com/benjchristensen/4670979
    #Java Futures

    View Slide

  9. #Java Futures
    https://gist.github.com/benjchristensen/4671081
    public static void run() throws Exception {
    ExecutorService executor =
    new ThreadPoolExecutor(4, 4, 1,
    TimeUnit.MINUTES,
    new LinkedBlockingQueue());
    try {
    // get f3 with dependent result from f1
    Future f1 = executor.submit(new CallToRemoteServiceA());
    Future f3 = executor.submit(new CallToRemoteServiceC(f1.get()));
    /* The work below can not proceed until f1.get()
    completes even though there is no dependency */
    // also get f4/f5 after dependency f2 completes
    Future f2 = executor.submit(new CallToRemoteServiceB());
    Future f4 = executor.submit(new CallToRemoteServiceD(f2.get()));
    Future f5 = executor.submit(new CallToRemoteServiceE(f2.get()));
    System.out.println(f3.get() + " => " + (f4.get() * f5.get()));
    } finally {
    executor.shutdownNow();
    }
    }

    View Slide

  10. #Java Futures
    https://gist.github.com/benjchristensen/4671081
    public static void run4() throws Exception {
    ExecutorService executor =
    new ThreadPoolExecutor(4, 4, 1,
    TimeUnit.MINUTES,
    new LinkedBlockingQueue());
    try {
    List> futures = new ArrayList>();
    // kick off several async tasks
    futures.add(executor.submit(new CallToRemoteServiceA()));
    futures.add(executor.submit(new CallToRemoteServiceB()));
    futures.add(executor.submit(new CallToRemoteServiceC("A")));
    futures.add(executor.submit(new CallToRemoteServiceC("B")));
    futures.add(executor.submit(new CallToRemoteServiceD(1)));
    futures.add(executor.submit(new CallToRemoteServiceE(2)));
    futures.add(executor.submit(new CallToRemoteServiceE(3)));
    // as each completes do further work
    for (Future> f : futures) {
    /* this blocks so even if other futures in the list
    complete earlier they will wait until this one is done */
    doMoreWork(f.get());
    }
    } finally {
    executor.shutdownNow();
    }
    }

    View Slide

  11. Multithreading is always complex˝
    Concurrency˝
    Java Futures are Expensive to Compose˝
    Java Futures˝
    Callbacks Have Their Own Problems˝
    Callbacks˝
    @benjchristensen from @netflix

    View Slide

  12. #Callbacks
    https://gist.github.com/benjchristensen/4677544
    ...
    // get f3 with dependent result from f1
    executor.execute(new CallToRemoteServiceA(new Callback() {
    @Override
    public void call(String f1) {
    executor.execute(new CallToRemoteServiceC(new Callback() {
    @Override
    public void call(String f3) {
    // we have f1 and f3 now need to compose with others
    System.out.println("intermediate callback: " + f3 + " => " + ("f4 * f5"));
    // set to thread-safe variable accessible by external scope
    f3Value.set(f3);
    latch.countDown();
    }
    }, f1));
    }
    }));
    ...

    View Slide

  13. Observables
    Subscribers
    Subscriptions
    Schedulers
    Operators
    #RxJava Guys:

    View Slide

  14. Observables
    The Observable object is who
    does the job.
    Represents an object that sends
    notifications (Provider) to a
    Subscriptor (Observer).

    View Slide

  15. Observables
    Add 2 missing semantics to the
    Observer pattern:
    #1: Emits a signal to the consumer
    when there is no more data available.
    #2: Emits a signal to the consumer
    when an error has occurred.

    View Slide

  16. Observables

    View Slide

  17. Observables

    View Slide

  18. Subscribers
    Subscribers provides a
    mechanism for receiving push-
    based notifications from
    Observables, and permits
    manual unsubscribing from
    these Observables.

    View Slide

  19. Subscribers
    Not an observer pattern:
    Observables often don't
    start emitting items until
    someone explicitly
    subscribes to them.

    View Slide

  20. Subscribers
    Observables often don't
    start emitting items until
    someone explicitly
    subscribes to them.

    View Slide

  21. Subscribers
    public class DefaultSubscriber extends rx.Subscriber {
    @Override public void onCompleted() {
    }
    @Override public void onError(Throwable e) {
    }
    @Override public void onNext(T t) {
    }
    }

    View Slide

  22. Subscriptions
    Subscriptions represents the link
    between your Observable and
    your Subscriber.
    #1: Subscriptions
    #2: CompositeSubscriptions

    View Slide

  23. #1: Schedulers.io()
    #2: Schedulers.computation()
    #3: Schedulers.from()
    Schedulers
    If you want to introduce multithreading into
    your cascade of Observable operators, you
    can do so by instructing those operators (or
    particular Observables) to operate on
    particular Schedulers.

    View Slide

  24. Operators
    Operators can be used in
    between the source Observable
    and the ultimate Subscriber to
    manipulate emitted items.
    You can even write your own
    custom operators.

    View Slide

  25. map()
    Transform the items emitted by an Observable
    by applying a function to each item.

    View Slide

  26. flatMap()
    Transforms the items emitted by an
    Observable into Observables, then flatten the
    emissions from those into a single Observable
    (no order)

    View Slide

  27. concatMap()
    Transforms the items emitted by an
    Observable into Observables, then flatten the
    emissions from those into a single Observable
    (keeps order)

    View Slide

  28. flatMap() vs concatMap()
    http://fernandocejas.com/2015/01/11/rxjava-observable-tranformation-concatmap-vs-flatmap/

    View Slide

  29. filter()
    Emits the same item it received, but only if it
    passes the boolean check (predicate).

    View Slide

  30. take()
    Emit only the first n items emitted by an
    Observable

    View Slide

  31. doOnNext()
    Allows us to add extra behavior each time an
    item is emitted.

    View Slide

  32. onError() is called if an
    exception is thrown at any time.
    Error handling
    The operators do not have to
    handle the exception.

    View Slide

  33. onErrorResumeNext()
    Instructs an Observable to emit a sequence of items if it
    encounters an error.
    onErrorReturn()
    Instructs an Observable to emit a particular item when it
    encounters an error.
    onExceptionResumeNext()
    Instructs an Observable to continue emitting items after it
    encounters an exception.
    retry()
    If a source Observable emits an error, resubscribe to it in the
    hopes that it will complete without error.
    retryWhen()
    If a source Observable emits an error, pass that error to another
    Observable to determine whether to resubscribe to the source.
    Error handling Operators

    View Slide

  34. #1: Observable and Subscriber can do
    anything
    #2: The Observable and Subscriber are
    independent of the transformational
    steps in between them.
    #3: Operators let you do anything to the
    stream of data.
    Key ideas behind RxJava
    http://blog.danlew.net/2014/09/15/grokking-rxjava-part-1/

    View Slide

  35. #1: Learning curve
    #2: Too many anonymous classes
    generated (OutOfMemory?)
    #3: Verbosity (retrolambda to the
    rescue?)
    But there are some pitfalls…

    View Slide

  36. #Example: Clean Architecture

    View Slide

  37. #Example: Clean Architecture

    View Slide

  38. @PerActivity
    public class UserListPresenter extends DefaultSubscriber>
    implements Presenter {
    private UserListView viewListView;
    private final UseCase getUserListUseCase;
    @Inject
    public UserListPresenter(@Named("userList") UseCase getUserListUserCase,
    UserModelDataMapper userModelDataMapper) {
    this.getUserListUseCase = getUserListUserCase;
    }
    @Override public void destroy() {
    this.getUserListUseCase.unsubscribe();
    }
    private void getUserList() {
    this.getUserListUseCase.execute(this);
    }
    ...
    }
    #Example: Reactive Presenter

    View Slide

  39. #Example: Reactive Presenter
    @PerActivity
    public class UserListPresenter extends DefaultSubscriber>
    implements Presenter {
    ...
    @Override public void onCompleted() {
    this.hideViewLoading();
    }
    @Override public void onError(Throwable e) {
    this.hideViewLoading();
    this.showErrorMessage(new DefaultErrorBundle((Exception) e));
    this.showViewRetry();
    }
    @Override public void onNext(List users) {
    this.showUsersCollectionInView(users);
    }
    }

    View Slide

  40. #Example: Clean Architecture

    View Slide

  41. #Example: UseCase
    public abstract class UseCase {
    private final ThreadExecutor threadExecutor;
    private final PostExecutionThread postExecutionThread;
    private Subscription subscription = Subscriptions.empty();
    protected UseCase(ThreadExecutor threadExecutor, PostExecutionThread postExecutionThread) {
    this.threadExecutor = threadExecutor;
    this.postExecutionThread = postExecutionThread;
    }
    protected abstract Observable buildUseCaseObservable();
    public void execute(Subscriber UseCaseSubscriber) {
    this.subscription = this.buildUseCaseObservable()
    .subscribeOn(Schedulers.from(threadExecutor))
    .observeOn(postExecutionThread.getScheduler())
    .subscribe(UseCaseSubscriber);
    }
    public void unsubscribe() {
    if (!subscription.isUnsubscribed()) subscription.unsubscribe();
    }
    }

    View Slide

  42. #Example: Execution Thread
    /**
    * MainThread (UI Thread) implementation based on a
    * {@link rx.Scheduler} which will execute actions on
    * the Android UI thread
    */
    @Singleton
    public class UIThread implements PostExecutionThread {
    @Inject
    public UIThread() {}
    @Override public Scheduler getScheduler() {
    return AndroidSchedulers.mainThread();
    }
    }

    View Slide

  43. #Example: UseCase
    /**
    * This class is an implementation of {@link UseCase} that represents a
    * use case for retrieving a collection of all {@link User}.
    */
    public class GetUserListUseCase extends UseCase {
    private final UserRepository userRepository;
    @Inject
    public GetUserListUseCase(UserRepository userRepository,
    ThreadExecutor threadExecutor,
    PostExecutionThread postExecutionThread) {
    super(threadExecutor, postExecutionThread);
    this.userRepository = userRepository;
    }
    @Override public Observable buildUseCaseObservable() {
    return this.userRepository.getUsers();
    }
    }

    View Slide

  44. #Example: Clean Architecture

    View Slide

  45. #Example: Operator and Action
    public class CloudUserDataStore implements UserDataStore {
    private final RestApi restApi;
    private final UserCache userCache;
    private final Action1 saveToCacheAction = new Action1() {
    @Override public void call(UserEntity userEntity) {
    if (userEntity != null) {
    CloudUserDataStore.this.userCache.put(userEntity);
    }
    }
    };
    public CloudUserDataStore(RestApi restApi, UserCache userCache) {
    this.restApi = restApi;
    this.userCache = userCache;
    }
    @Override public Observable> getUserEntityList() {
    return this.restApi.getUserEntityList();
    }
    @Override public Observable getUserEntityDetails(final int userId) {
    return this.restApi.getUserEntityById(userId).doOnNext(saveToCacheAction);
    }
    }

    View Slide

  46. #Example: Data transformation
    @Singleton
    public class UserDataRepository implements UserRepository {
    private final UserDataStoreFactory userDataStoreFactory;
    private final UserEntityDataMapper userEntityDataMapper;
    private final Func1, List> userListEntityMapper =
    new Func1, List>() {
    @Override public List call(List userEntities) {
    return UserDataRepository.this.userEntityDataMapper.transform(userEntities);
    }
    };
    private final Func1
    userDetailsEntityMapper = new Func1() {
    @Override public User call(UserEntity userEntity) {
    return UserDataRepository.this.userEntityDataMapper.transform(userEntity);
    }
    };
    @Override public Observable> getUsers() {
    final UserDataStore userDataStore = this.userDataStoreFactory.createCloudDataStore();
    return userDataStore.getUserEntityList().map(userListEntityMapper);
    }
    }

    View Slide

  47. #Example: Observable creation
    @Override public Observable> getUserEntityList() {
    return Observable.create(new Observable.OnSubscribe>() {
    @Override public void call(Subscriber super List> subscriber) {
    if (isThereInternetConnection()) {
    try {
    String responseUserEntities = getUserEntitiesFromApi();
    subscriber.onNext(userEntityJsonMapper.transformUserEntityCollection(
    responseUserEntities));
    subscriber.onCompleted();
    } catch (Exception e) {
    subscriber.onError(new NetworkConnectionException(e.getCause()));
    }
    } else {
    subscriber.onError(new NetworkConnectionException());
    }
    }
    });
    }

    View Slide

  48. #1: Good starting point to switch to Rx
    Observables.
    #2: No need to deal with threading an
    synchronization.
    #3: Very simple to wrap an http
    connection in an Observable
    How do I start with RxJava?
    Rx at data level

    View Slide

  49. #1: We can convert our events into Rx
    Observables
    How do I start with RxJava?
    Rx at view level
    Observable input = Observable.FromEventPattern(textView, "TextChanged")
    .Select(_ => textbox.Text)
    .Throttle(TimeSpan.FromSeconds(0.5))
    .DistinctUntilChanged();

    View Slide

  50. #1: You will return Rx Observables in
    domain layer.
    #2: Be careful with side effects (Rx
    Schedulers other than UI Thread)
    How do I start with RxJava?
    Rx at domain level

    View Slide

  51. #1: By default, RxJava is synchronous.
    #2: onSubscribe() is executed separately
    for every new subscriber.
    #3: Subscriptions leak memory.
    Tips and Tricks
    #4: Read the official documentation

    View Slide

  52. References
    Reactive Programming on Android With RxJava
    https://mttkay.github.io/blog/2013/08/25/functional-reactive-programming-on-android-with-rxjava/
    Grokking RxJava
    http://blog.danlew.net/2014/09/15/grokking-rxjava-part-1/
    Reactive Programming in the Netflix API with RxJava
    http://techblog.netflix.com/2013/02/rxjava-netflix-api.html
    Rx for .NET and RxJava for Android
    http://futurice.com/blog/tech-pick-of-the-week-rx-for-net-and-rxjava-for-android
    https://github.com/android10/Android-CleanArchitecture
    Official Documentation
    https://github.com/ReactiveX/RxJava/wiki
    https://github.com/android10/Android-ReactiveProgramming

    View Slide

  53. ?
    ˝
    Source: US Census Bureau
    Questions

    View Slide

  54. @fernando_cejas
    http://soundcloud.com/jobs
    Thanks!

    View Slide