Recently I started android project with hard usage of Reactive extensions. I've read some introductions and tutorials, but I'm still at beginner's level. According to this article:
everything is a stream
however my current understanding (or mental barrier) tells me that any operation which mutates state (removing data from repository for example) should not be/return a stream/observable.
Little background about my domain: I have a use case for registering geofences. Since geofences do not survive reboot, I keep track of active geofences in repository. Sometimes app needs to remove geofence, so basic steps to achieve this are:
retrieve geofence from repository
remove geofence from device
remove geofence from repository
my current solution is following:
geofenceRepository.get(id)
.map(new Func1<Geofence, String>() {
#Override
public String call(Geofence geofence) {
geofenceRepository.delete(geofence.getId()); // synchronous call here
return geofence.getRequestId();
}
})
.toList()
.flatMap(new Func1<List<String>, Observable<Status>>() {
#Override
public Observable<Status> call(List<String> ids) {
return locationProvider.removeGeofences(ids);
}
});
where Geofence is my custom data structure and locationProvider is from this nice library.
You'll notice that data retrieval is implemented as stream/observable unlike delete.
What I don't like in above example is: map operator with side effect
Questions
What would be better solution to be more "reactive", what I'm missing here?
Does it make sense to use reactive approach at all?
by reactive programming I mean:
programming with asynchronous data streams
I don't see any problem with your approach and being more reactive would mean more API uses/returns Observables. You can have side-effects in any of the lambdas but be careful when you mutate a value since if asynchrony is involved, the same object may be mutated at the same time at different stages of the pipeline. Usually, we use immutable or effectively immutable values to avoid this problem. There is no real need to split your activities so the suggested doOnNext separation is a preference of the particular developer
If your geofenceRepository.delete had a version that returns an Observable of some sort, you could go more reactive by flatMapping over it:
get(id)
.flatMap(f -> geoFence.deleteAsync(f.getId()).map(v -> f.getRequestId()))
.toList()
.flatMap(...)
.subscribe(...)
Here, deleteAsync would return an Observable<Void> which when completes, will resume the main sequence with the requestId.
Reactive is great and I think this situation is perfect.
I think what you really want to do here is make sure each of your operators does exactly 1 thing. Like you said, the flatMap is also removing your geofence.
Try using the onNext operator in your chain for the removal. What you want to do it retrieve it, which it looks like geofenceRepository.get(id), remove it with an operator, then remove it from the locationProvider. Maybe something like:
geofenceRepository.get(id)
.map(new Func1<Geofence, String>() {
#Override
public String call(Geofence geofence) {
return geofence.getRequestId();
}
})
.doOnNext(new Action1<String>){
#Override
public void call(final String geoFenceId) {
geofenceRepository.delete(geofence.getId());
}
})
.doOnNext(new Action1<String>() {
#Override
public void call(final String geoFenceId) {
return locationProvider.removeGeofences(ids);
}
});
What you probably really want to do is create two subscribers. That way if you want to watch the status of one or both you can. You could combine the status of each. It depends a bit on if deleting from the repository and deleting from the provider are independent.
Observable<String> getFence = geofenceRepository.get(id)
.map(new Func1<Geofence, String>() {
#Override
public String call(Geofence geofence) {
return geofence.getRequestId();
}
});
getFence.subscribe(new Action1<String>){
#Override
public void call(final String geoFenceId) {
geofenceRepository.delete(geofence.getId());
}
});
getFence.map(new Func1<String, Status>() {
#Override
public Status call(final String geoFenceId) {
return locationProvider.removeGeofences(ids);
}
}).subscribe(new Action1<Status>(){
#Override
public void call(final Status status(){
//Handle your status for each removal
}
});
Related
Let me describe my situation:
I want to register new records via an API.
I want to update some records via an API.
I need to be notified when all of these requests have finished, to start another task.
Specifically I have two ArrayList:
ArrayList<Report> createdReports = myHelper.getOfflineCreatedReports();
ArrayList<Report> editedReports = myHelper.getOfflineEditedReports();
Each report can use methods to get Observable instances from my ApiService (Retrofit implementation).
Observable<NewReportResponse> createdReportsObs = Observable.from(createdReports) // .just() != .from()
.flatMap(new Func1<Report, Observable<NewReportResponse>>() {
#Override
public Observable<NewReportResponse> call(Report report) {
return report.postToServer();
}
});
Observable<NewReportResponse> editedReportsObs = Observable.from(editedReports)
.flatMap(new Func1<Report, Observable<NewReportResponse>>() {
#Override
public Observable<NewReportResponse> call(Report report) {
return report.updateInServer();
}
});
I am using the flatMap operator to get one Observable for each report.
But I am not sure how to wait until all of the requests have finished.
I was thinking in using the zip operator.
Observable.zip(createdReportsObs, editedReportsObs, new Func2<NewReportResponse, NewReportResponse, Boolean>() {
#Override
public Boolean call(NewReportResponse justOneResponse, NewReportResponse justOneResponse2) {
return false;
}
});
Unfortunately I saw some examples where zip is used to create pairs of Observables.
Please suggest me what operator I can use to achieve it. Or how to do it using rxJava with a different approach.
Thank you in advance.
Are you using RxJava 2? If so you can use the new completable api. This is assuming you don't need to know any of the server results, just need to wait for them to complete.
Completeable.merge(createdReportsObs.toCompleteable(),
editedReportsObs.toCompleteable())
.subscribe()
This is my way. May not best practice.
Observable.merge(createdReportsObs, editedReportsObs)
.toList()
.flatMap(Observable::from)
.xxx //Now they are completed, do what you want
.subscribe();
I have a searchBar (an EditText) with four tabs below it (each tab should display different results). I'm using RxJava with RxBinding to listen and react to text changes events, and I'm using switchMap() operator to execute a Retrofit service for each text change emission.
Since user can select any of the four tabs I actually execute the corresponding Retrofit request for that tab.
For each of those Retrofit services I receive a different response object.
How can I handle different return types inside switchMap() since the last one needs a common type for all?
I have already asked a similar question previously but the answer while it works doesn't lets me to consume the data from my subscriber. Or is my approach wrong from the beginning and I should try a different approach ?
Code :
RxTextView.textChangeEvents(searchbar.getEditText())
.debounce(400, TimeUnit.MILLISECONDS)
.filter(new Func1<TextViewTextChangeEvent, Boolean>() {
#Override
public Boolean call(TextViewTextChangeEvent text) {
return (text.text().length() > 2);
}
})
.subscribeOn(AndroidSchedulers.mainThread())
.observeOn(Schedulers.io())
.switchMap(new Func1<TextViewTextChangeEvent, Observable<Void>>() {
#Override
public Observable<Void> call(TextViewTextChangeEvent textViewTextChangeEvent) {
String searchBarText = textViewTextChangeEvent.text().toString();
switch (visibleTab) {
case TAGS:
presenter.executeSearchPostsByTag(searchBarText, String.valueOf(0));
case PEOPLE:
return presenter.executeSearchPostsByPeople(searchBarText, String.valueOf(0));
case COMPANIES:
return presenter.executeSearchPostsByCompanies(searchBarText, String.valueOf(0));
case JOBS:
return presenter.executeSearchPostsByJobs(searchBarText, String.valueOf(0));
default:
return presenter.executeSearchPostsByTag(searchBarText, String.valueOf(0));
}
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Void>() {
#Override
public void onCompleted() {
Timber.i("ON COMPLETED");
}
#Override
public void onError(Throwable e) {
Timber.i("ON ERROR e : %s", e.getMessage());
}
#Override
public void onNext(Void aVoid) {
Timber.i("ON NEXT");
}
});
In the code above you 'll see that I have return type of Observable but that doesn't works I just added it so you 'll see what I'm doing.
Thing is, do any of the executeSearchPostsBy* methods return a non-empty Observable? If all of their Observables are empty, then you can just tack on .cast(Void.class) to all of them. If they do return non-empty observables but you don't care about the items, then tack on .ignoreElements().cast(Void.class).
If you need to do some processing for anything that is returned, then you should do that in different methods, in their own Observable chains.
If you need to do some processing that is common to all of them, then you need to adjust your model to reflect this, even if it's just wrapper classes.
I am new at RxJava and I have some pain to execute my first 'difficult' query.
I have two Observables generated from Retrofit, one that 'ping' a new api, the other the old one. The first one will query 'http://myurl.com/newapi/ping', the second one 'http://myurl.com/oldapi/ping'. Result from this request doesn't matter, I just want to know if the server is using the new or old api.
So I would like to call both observables at the same time, and finally have a boolean at the end to know if I'm using old or new api.
I tried something like that
Observable.mergeDelayError(obsOldApi,obsNewApi)
.observeOn(AndroidSchedulers.mainThread(), true)
.subscribeOn(Schedulers.io())
.subscribe(new Subscriber<String>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(String s) {
}
});
But onError will be called once (I would like it to be called only if both request failed) and when onNext is called, I don't know from which request it came (old or new api ?).
Thank you for you help
For simplicity, let say that you'll received "NEW" or "OLD" regarding which api is available.
The difficulty of your operation is to manage errors : RxJava deals errors as terminal state. So you'll have to ignore this error, using .onErrorResumeNext() for example.
Observable<String> theOld = oldApi.map(r -> "OLD")
// ignore errors
.onErrorResumeNext(Obervable.empty());
Observable<String> theNew = newApi.map(r -> "NEW")
.onErrorResumeNext(Obervable.empty());
Observable.merge(theOld, theNew)
.first() // if both api are in errors
.subscribe(api -> System.out.println("Available API : "+api));
I added the operator first : it will take only the first result ("OLD" or "NEW") but trigger an error if the previous Observable is empty, which is the case if both API are unavaible.
I have an API call and I want to wrap it using Observable:
private Observable<RealmResults<Account>> getAccounts() {
final Observable<RealmResults<Account>> realmAccounts =
Observable.defer(new Func0<Observable<RealmResults<Account>>>() {
#Override
public Observable<RealmResults<Account>> call() {
return RealmObservable.results(getActivity(), new Func1<Realm, RealmResults<Account>>() {
#Override
public RealmResults<Account> call(Realm realm) {
return realm.where(Account.class).findAll();
}
});
}
});
return Observable
.create(new Observable.OnSubscribe<RealmResults<Account>>() {
#Override
public void call(final Subscriber<? super RealmResults<Account>> subscriber) {
DataBridge.getAccounts(Preferences.getString(Constant.ME_GUID, ""), new OnResponseListener() {
#Override
public void OnSuccess(Object data) {
Log.d("Stream", "onSuccess");
realmAccounts.subscribe(subscriber);
}
#Override
public void onFailure(Object data) {
subscriber.onError(new Exception(data.toString()));
}
});
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.startWith(realmAccounts);
}
and I use it like
Observable<Accounts> accounts = getAccounts().flatMap(
new Func1<RealmResults<Account>, Observable<Account>>() {
#Override
public Observable<Account> call(RealmResults<Account> accounts) {
return Observable.from(accounts);
}
});
How can I use the accounts observable multiple times without calling the API each time. I need to process the stream of accounts and extract different sets of data out of it.
The easiest method is to use operator cache, which internally uses ReplaySubject. It cache the source observable items and then serve the results from cache.
...
Observable<<RealmResults<Account>> cachedResult = getAccounts().cache();
Observable<Accounts> accountsObservable = cachedResult.flatMap(...);
Observable<X> xObservable = cachedResult.flatMap(...);
If you would like to avoid caching results you should use Connectable Observables. Usually it only does matter for Hot Observables. Connectable observable does not begin emitting items until its Connect method is called. You can use publish operator to convert to Connectable Observable.
ConnectableObservable<<RealmResults<Account>> connectebleObservable = getAccounts().publish();
Observable<Accounts> accountsObservable = connectebleObservable .flatMap(...);
Observable<X> xObservable = connectebleObservable .flatMap(...);
//You must subscribe before connect
accountsObservable.subsribe(...);
xObservable.subscribe(...);
//start emiting data
connectebleObservable.connect();
The important catch here is that you must subscribe before connect - to avoid data loss - otherwise you must use replay operator, which is similar to cache operator, but used for connectable observable
And what about share ?
It create ConnectableObservable and exposes it as regular Observable. First subscription automatically causes connection and emission.
Share used in your case, without replay may cause data loss or multiple executions depending on timing.
for example for 2 subscribers and one item int the stream you may have fallowing cases:
2 subscriptions created before onNext - works as expected.
second subscription created after onNext but before onComplete - second subscription gets only onComplete
second subscriptinon created after onComplete - 2 executions wihtout caching
I'm trying to use rx-java on Android to do few sequential http requests, each of which is dependent of the response of the former one.
This does not quite fit the map() / doFinall() model and so I'm not sure what would be the best way to do this without getting into "callback hell" as well as writing concise code.
More concretely:
do http GET "/x"
do http GET "/y" if (2) was successfully
do calculation on the result of GET /y
Any suggestions on how to go about this?
I think flatMap is what you're looking for. For example, assuming you have the following methods:
Observable<Foo> getFoo();
Observable<Bar> getBar(Foo foo); //needs a Foo first
You could effectively chain them this way:
getFoo().flatMap(new Func1<Foo, Observable<Bar>>() {
#Override
public Observable<Bar> call(Foo foo) {
return getBar(foo);
}
});
You could then perform some calculation with the final result Bar by subscribing to the resulting Observable<Bar> (full example shown for clarity):
getFoo().flatMap(new Func1<Foo, Observable<Bar>>() {
#Override
public Observable<Bar> call(Foo foo) {
return getBar(foo);
}
}).subscribe(new Action1<Bar>() {
#Override
public void call(Bar bar) {
//everything succeeded, so perform calculation to the Bar
}
}, new Action1<Throwable>() {
#Override
public void call(Throwable throwable) {
//handle an error that occurred anywhere in the chain
}
});
Note that an error anywhere in the process of getting the Foo or the Bar will be handled by the Action1 that we provide when subscribing to the Observable. It is, of course, painfully verbose because Java, but at least it avoids nesting Observables/callback hell.