RxJava 2.x: Should I use Flowable or Single/Completable?
Asked Answered
F

6

42

I'm developing an Android app using Clean Architecture and I'm migrating it to RxJava 2.x. I have to make some network requests to a soap service, so I defined the api interface in the domain module:

public interface SiginterApi {
    Observable<User> login(String user, String password);
    ...
    Observable<List<Campaign>> getCampaigns(List<Long> campaignIds);
}

I've read that a network request should be made with "Flowable", because of the backpressure management since it's a 'cold observable'. On the other hand, I know the result of the request will be success (with the response) or error, so I don't know if I should use Flowable or Single or even Observable.

Furthermore, I have a database accesses like this:

public interface UserRepository extends Repository {
    Observable<Void> saveUser(String username, String hashedPassword, boolean logged, User user);
    ...
    Observable<User> findUser(String username, String hashedPassword);
}

I don't know if I should use Completable/Flowable/Observable in saveUser method and Single/Flowable/Observable in findUser method.

Fregger answered 1/3, 2017 at 7:25 Comment(0)
O
39

Backpressure is what you get when a source Observable is emitting items faster than a Subscriber can consume them. It's most often a concern with hot observables, not cold ones like your network requests.

I think you should use Completable instead of Observable<Void> in your saveUser method, and use Single for all places where you follow a request/response or input/output pattern. Observable should be used when you actually want a continuous stream of events.

Oscillogram answered 1/3, 2017 at 8:34 Comment(5)
Thanks for the response. I agree that Single would be the better option to network requests, because of the single response, but as you can read in this question and many other blogs about RxJava 2.x, network and database accessors should be made with Flowable.Goethe
I believe the author is referring to network and database connections which do behave like a stream of events. If I understand correctly, that's different from a HTTP request/response pair or a single database query.Oscillogram
So you think that the key to decide between an Observable (Single/Completable) and Flowable is whether you actually need backpressure control or not. It doesn't mind the source type (cold source, network/database accessors...)Goethe
Yes - if your "stream" emits one (Single) or zero (Completable) values, you don't need backpressure handling. If you have e.g. an open connection to a server and continuously receive data from it, you should use a Flowable to handle the possibility of data arriving faster than you can process it.Oscillogram
Correction: Single and Completable are present in 1.x and Retrofit for RxJava 1 also supports them.Treviso
J
18

Backpressure occurs when an Observable is emitting items more rapidly than an operator or subscriber can consume them.

Knowing that, Backpressure is not an issue in your case as your Observable will emit only one item so Flowable is not a good candidate.

So the real question is whether to use Completable or Observable for saveUser and Single or Observable for findUser and here as only one result is expected (success or failure) for the sake of simplicity and clarity of your API, you should definitively use Completable/Single otherwise it will be hard to understand that only one value will be emitted which could be misleading to your API users.

Jowett answered 1/3, 2017 at 8:38 Comment(2)
Thanks for the response. I agree that Single would be the better option to network requests, because of the single response, but as you can read in this question and many other blogs about RxJava 2.x, network and database accessors should be made with Flowable.Goethe
When to use Flowable. The example related to db access is I quote Reading from a database through JDBC is also blocking and pull-based and is controlled by you by calling ResultSet.next() for likely each downstream request. In this case we read the db and we have several results which is not your case and if you check well the doc, the question is more what to chose between Observable and Flowable so when you have several items to emitJowett
N
10

Cardinality is one way of understanding the differences between Completable, Maybe and Single:

  • A Maybe<T> is just an Observable with cardinality 0 or 1 i.e. it represents a result which can either be present or not.
  • A Single<T> is an Observable that always returns a result i.e. a cardinality of 1.
  • A Completable can be interpreted sort of as a Observable<Void> i.e. a cardinality of 0.

So in your case you can change the signature of the repository in this way:

Completable saveUser(...);

Single<User> findUser(...);

(I didn't mention Flowables which are like Observables with backpressure).

Nonsuit answered 4/4, 2018 at 10:13 Comment(0)
F
3

As I understand, you should use Single: when you are pretty sure that you are going to get an item, otherwise you would get an error. Eg: GET - card/:id

Maybe: is the correct solution if you are no so sure if you will get an item. Eg: GET - card?license-plate=xvar3

Completable: when you only want to know if the action was made. Eg: PUT or DETELE

Observable: when the quantity of items is not so large.

Flowable: when you don't konw the quantity of items that you will get.

Festus answered 21/2, 2018 at 22:20 Comment(0)
A
0

Hmm...

I think the question isn't a trivial one while you have faced a more complex situation.

Eg. Save user (REST) > Save user (SQLlite)

You may want to chain Rx streams into one.

So either you declare

1.

Flowable<Response<Void>> saveUser(String username, String hashedPassword, boolean logged, User user);

and then use some of: flatMap, concatMap, switchMap

2.

... or I think maybe more preferable to not confuse class responsibility (you may use the same code in many places)

Single<Response<Void>> saveUser(String username, String hashedPassword, boolean logged, User user);

RestService.saveUser(...)
.toFlowable() // Transform into Flowable 
.switchMap{ saveToDB() }
.subscribeBy{ ... }
.addTo( yourDisposable )

3.

By the way, I suggest to not use Completable in case if you want to have nice error handling. You may easily wrap Retrofit.Response<Body> in Single or Flowable to take advantage of the code response from server

Asaasabi answered 27/11, 2017 at 14:32 Comment(0)
D
0
  • If your Observable (service) is emitting items at a faster rate than your observer (client) then you should use Flowable to benefit from the back pressure mechanism. Hot Observable
  • If your service will emit once per request and on demand data (most case of API) then it should be treated as a cold observable. In this case go for Single or Maybe. The difference is that if you want to handle the case where the service send no response or error in this case Single is better to use by leveraging the onError() callback. If you don't case if your service will fail or succeed and you don't mind an empty emission and go for Maybe.
  • 99% of database WRITE requests return nothing (only if you want to ensure your data by returning a boolean) in this use case I would use Completable to execute the action and call onComplete() at the end.
Dashboard answered 18/12, 2019 at 20:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.