0

My question will be more design or architecture related. Just need some advice from more experienced engineers.

I'm learning RxJava/RxAndroid now and would like to replace my asynchronous logic in an app based on lessons learned.

I have an app based on MVP pattern and almost every Presenter communicates with Repository. To avoid ANRs I use some async logic inside a Repository class (it is actually not critical what mechanism is exactly used: ThreadPoolExecutor, AsyncTask or JobScheduler).

So I decided to replace that async logic with a set of RxJava classes, but I faced with one interesting question. Where should I implement that async logic from the architectural point of view?

IMHO, there are two possible options (may be there are others):

  1. Rewrite every Repository and replace all custom async logic with new Reactive one;
  2. Remove all async logic from Repository (make a class fully synchronous) and move it to some external entity (lets name it "RepositoryAsyncRunner").

If I follow Case #1, then there is a need to change interfaces for every repository and it will work at the same way. Looks like as unnecessary change, because logic has not changed. The only thing has changed is the way of async execution.

If I follow Case #2, then my Presenter will communicate with that async wrapper instead of a plain Repository and all async logic will be encapsulated in a separate class.

Frankly speaking I like the second approach. If I'll change my mind later and want to replace RxJava with another cool library, I have to change only AsyncRunner without modification of Repository class, its interface and related Unit Tests (may be even Presenter's tests won't be changed). Also using this approach I will have two different classes for two different purposes ('S' in SOLID :)

Will be glad to get some help to resolve this situation.

akarnokd
  • 69,132
  • 14
  • 157
  • 192

1 Answers1

0

If you use RxJava but your repository does not expose Rx primitives like Observable, Single etc. you will miss out on some of the benefits of RxJava like chainability, transformations in a pipeline, no nested callbacks etc.

So if you are to obtain the best result from using RxJava your repositories should probably have methods that return Rx primitives.

However, if you are worried about the possibility of having to move to another asynchronous composition framework you can always use the Java asynchronous task primitive which is Callable. These are common and are easily converted between the various frameworks.

Example:

public class CallableFactory {

    private final RetrofitService retrofitService;

    public CallableFactory(RetrofitService retrofitService) {
        this.retrofitService = retrofitService;
    }

    public Callable<String> getCallable(final Integer id) {
        return new Callable<String>() {
            @Override
            public String call() throws Exception {
                return retrofitService.getById()
                        .enqueue();
            }
        };
    }
}

Then the Rx repository would look like this:

class RealRepository implements RxRepository {

    private final CallableFactory callableFactory;

    @Inject
    RealRepository(CallableFactory callableFactory) {
        this.callableFactory = callableFactory;
    }

    @Override
    public Observable<String> retrieveById(final Integer id) {
        return Observable.fromCallable(callableFactory.getCallable(id));
    }
}

And if you had to move to, say, Google Guava and use ListenableFuture instead of Rx you could re-use the Callable:

class RealGuavaRepository implements GuavaRepository {

    private final CallableFactory callableFactory;

    @Inject
    RealRepository(CallableFactory callableFactory) {
        this.callableFactory = callableFactory;
    }

    @Override
    public ListenableFuture<String> retrieveById(final Integer id) {
        return MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor())
                .submit(callableFactory.getCallable(id));
    }
}

The hard dependency on the Executor in the final example is not ideal but it is a simplification - you could inject these quite easily.

EDIT: if you are talking about an Android project there is a good example in the official Google Android Architecture Blueprints here

David Rawson
  • 20,912
  • 7
  • 88
  • 124
  • Thanks for your reply. I was thinking, that someone will point to one of the main advantages of RX in context of "chainability, transformations in a pipeline, no nested callbacks".I just afraid that "callback hell" could be transformed to "observable hell", when each component will add some additional processing logic in a chain, run it on several different Schedulers, etc. So I wanted to restrict async points in the app and perform that async communication only in context of Presenter<-> Model relation, but not inside Model. Model can by synchronous. Maybe I just complicate everything. – Daniil Polyakov Mar 04 '18 at 07:08
  • @DaniilPolyakov I updated my answer to include a good example of RxJava in an Android project. Any of the frameworks, if you do the architecture carefully you won't have to worry. – David Rawson Mar 04 '18 at 07:16
  • Thanks for your help! I will take a look to those samples. I definitely will mark your answer as "Accepted". – Daniil Polyakov Mar 04 '18 at 07:21