Basically I need to create a List of Observables without initial values. I subscribe to that list of Observables and will provide required results based on the responses from all Observables. I use zip operator.
The problem is that I need to create Observables initially, add them to the list and use zip operator. Only later I do network request with Retrofit and I need to update the value of observable in the list so my whole zip operator will be working.
However, I did not find a way to force update an observable in the list with the response from Retrofit. It seems very easy but I did not found any solutions.. only idea is to use tons of subjects and add them to the list instead...
List<Observable<Object>> listObservables = new ArrayList<>();
//Adding initial request
Observable<Object> testObservable = RetrofitFactory.create().startProcess();
listObservables.add(testObservable);
Observable.concatDelayError(listObservables).subscribe(response ->
{
//This is where all results should be managed
Log.d("response", String.valueOf(response));
},
error ->
{
Log.d("response", String.valueOf(error));
});
//Actual request occurs much later in application
listObservables.get(0).subscribeOn(Schedulers.io()).
observeOn(AndroidSchedulers.mainThread()).subscribe(response ->
{
// the response of this, should notify concatDelayError
Log.d("respoonse", String.valueOf(response));
});
If I understand correctly, you want to implement sub requests model. For this task you can break chain of operators to different execution flows and combine it back, for example, with zip operator. With this approach you can create completely independent data flow with sole trigger.
Subject<Event> eventSubject = PublishSubject.create();
Observable<TriggerObject> mainRequest = eventSubject.flatMap((event) ->
RetrofitFactory.create().startProcess());
Observable<FirstSubResult> firstSubRequest = mainRequest.flatMap(tigger -> {
// make first sub request
});
Observable<SecondSubResult> secondSubRequest = mainRequest.flatMap(tigger -> {
// make second sub request
});
Observable<CompleteResult> resultObservable = Observable.zip(firstSubRequest, secondSubRequest,
// zip function
(first, second) -> {
// combine result of sub requests to complete result object
});
Now you can start request flow by your event:
// post your event. On button clicked for evxample
eventSubject.doOnNext(yourEvent);
NOTE: this answer show main idea of chaining data flow sequences. This applicable to to other types of requests, and you can use this approach without retrofit
Related
private fun getAllFollowers(user: User){
val items = user.items
val requests = ArrayList<Observable<List<Followers>>>()
for (item in items!!.iterator()) {
requests.add(AutoCompleteApiProvider.getFollowersList(item.followersUrl))
}
//here ZIP is not resolving
Observable.zip(requests, Function<ArrayList<Followers>,java.util.ArrayList<java.util.ArrayList<Followers>>>(){
var allResponse = java.util.ArrayList<java.util.ArrayList<Followers>>()
allResponse.add(it)
return#Function allResponse
})
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(Action1<ArrayList<ArrayList<Followers>>> { time -> Log.d(TAG, "I will call adapter here") },
Action1<Throwable> { throwable -> Log.d(TAG, "error handing part here")})
}
here zip operator is not resolving. I am not sure that i am passing right params to zip operator.
I want to perform following task.
Search for users by their name using the GitHub API (sorted by the
number of followers) and display the results in a RecyclerView
so As user type in editText, I am calling Github API which gives username suggestion but I want to sort this name as per the number of followers each name has.
to get followers to count I need to make separate Github API call with userName as query params and API will respond with followers object. I will count this followers object.
here the tricky part is if search suggestion API gives 30 users in response.
I will have to make 30 network call parallelly and will have to wait until the response of each API call. once I have 30 response than I will count followers of each user and perform followers wise sorting and shows the result in recyclerview
so once I have username suggestion. I make API call to get followers using rxJava ZIP operator.
I am making a list of Observable<List<Followers>> and passing this list to zip operator here my question which function I should pass as second args in zip operators. any suggestion around this?
rest code is complete but i stuck at zip operator.
variable requests is a ArrayList of observable
val observableFun = Observable.zip(requests, Function<Array<Any>,
HashMap<ItemsItem, ArrayList<Followers>>> { t ->
/* write your own logic to play around combine response of all observalble*/
return#Function allResponse
})
and subscribe this observableFun.
I am very new to using RxJava with Retrofit in Android. I have successfully written the API calls and developed the interface too. Now, I want to write my code in a way that I can send two requests: second request depending upon the values of first request. Can someone guide me if this is possible? If so then how? Any code snippet will really be helpful.
For example: following are two requests:
mCompositeDisposable.add(fcService.getStationList()
.subscribeOn(Schedulers.io()) // "work" on io thread
.observeOn(AndroidSchedulers.mainThread()) // "listen" on UIThread
.subscribe(this::handleResults, this::handleError)
);
mCompositeDisposable.add(fcService.getStationSensor("12345678")
.subscribeOn(Schedulers.io()) // "work" on io thread
.observeOn(AndroidSchedulers.mainThread()) // "listen" on UIThread
.subscribe(this::handleResults, this::handleError)
);
Second request is possible with the value from the first request's response. Is it possible to merge these two requests in a way that I write code only once for them?
With the flatMap operator you can check the response of the first call and choose the next action to follow, in this way you build a new Observable that you can subscribe to (The next "code" is kotlin style):
Single<StationSensor> newSingle =
fcService.getStationList().flatMap{ stationList ->
when(stationList){
"OK_value" -> fcService.getStationSensor(stationList)
else -> Single.error(RuntimeException("Error response"))
}
}
I want to asynchronously retrieve data via multiple REST APIs. I'm using Retrofit on Android with the rxJava extension, i.e. I execute any GET request by subscribing to an Observable.
As I said, I have multiple source APIs, so when the first source does not yield the desired result I want to try the next on, if that also fails, again try the next and so forth, until all sources have been queried or a result was found.
I'm struggling to translate this approach into proper use of Observables since I don't know which operators can achieve this behaviour and there are also some constraints to honor:
when a result has been found, the remaining APIs, if any, should not be queried
other components depend on the result of the query, I want them to get an Observable when starting the request, so this Observable can notify them of the completion of the request
I need to keep a reference to aforementioned Observable because the same request could possibly be made more than once before it has finished, in that case I only start it the first time it is wanted and subsequent requests only get the Observable which notifies when the request finished
I was starting out with only one API to query and used the following for the request and subsequent notification of dependent components:
private Observable<String> loadData(int jobId) {
final ConnectableObservable<String> result = Async
.fromCallable(() -> getResult(jobId))
.publish();
getRestRequest()
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
dataHolder -> {
if (dataHolder.getData() != null && !dataHolder.getData().isEmpty()) {
saveData(dataHolder.getData());
} else {
markNotFound(dataHolder);
}
},
error -> currentJobs.remove(jobId),
() -> {
currentJobs.remove(jobId);
result.connect();
});
return result;
}
This code was only called for the first request, the returned Observable result would then be saved in currentJobs and subsequent requests would only get the Observable without triggering the request again.
Any help is highly appreciated.
Assuming you have a set of observables that re-connect each time you subscribe:
List<Observable<Result>> suppliers = ...
Then you just need to do the logical thing:
Observable<Result> results = Observable
.from(suppliers)
.concatMap(supplier -> supplier)
.takeFirst(result -> isAcceptable(result))
.cache()
Use .onErrorResumeNext, and assuming that each service observable may return 0 or 1 elements use first to emit an error if no elements are emitted:
Observable<T> a, b, c;
...
a.first().onErrorResumeNext(t -> b.first())
.onErrorResumeNext(t -> c.first())
.onErrorResumeNext(t -> d.first())
...
In my Android app I am using domain level Repository interface, which is backed with local DB implemented using SqlBrite and network api with Retrofit observables. So I have method getDomains(): Observable<List<Domain>> in Repository and two corresponding methods in my Retrofit and SqlBrite.
I don't want to concatenate or merge, or amb these two observables. I want my Repository to take data only from SqlBrite and since SqlBrite returns QueryObservable, which triggers onNext() every time underlying data changed, I can run my network request independently and store results to SqlBrite and have my Observable updated with fetched from network and stored to DB data.
So I tried to implement my Repository's getDomains() method as follow:
fun getDomains(): Observable<List<Domain>> {
return db.getDomains()
.doOnSubscribe {
networkClient.getDomains()
.doOnNext { db.putDomains(it) }
.onErrorReturn{ emptyList() }
.subscribe()
}
}
But in this case every time the client should subscribe, every time it would make network requests, that is not so good. I thought about other do... operators to move requests there, but doOnCompleted() in case of QueryObservable would never be called, until I call toBlocking() somewhere, which I won't, doOnEach() also not good as it makes requests every time item from db extracted.
I also tried to use replay() operator, but though the Observable cached in this case, the subscription happens and results in network requests.
So, how can combine these two Observables in the desired way?
Ok, it depends on the concrete use case you have: i.e. assuming you want to display the latest data from your local database and from time to time update the database by doing a network request in the background.
Maybe there is a better way, but maybe you could do something like this
fun <T> createDataAwareObservable(databaseQuery: Observable<T>): Observable<T> =
stateDeterminer.getState().flatMap {
when (it) {
State.UP_TO_DATE -> databaseQuery // Nothing to do, data is up to date so observable can be returned directly
State.NO_DATA ->
networkClient.getDomains() // no data so first do the network call
.flatMap { db.save(it) } // save network call result in database
.flatMap { databaseQuery } // continue with original observable
State.SYNC_IN_BACKGROUND -> {
// Execute sync in background
networkClient.getDomains()
.flatMap { db.save(it) }
.observeOn(backgroundSyncScheduler)
.subscribeOn(backgroundSyncScheduler)
.subscribe({}, { Timber.e(it, "Error when starting background sync") }, {})
// Continue with original observable in parallel, network call will then update database and thanks to sqlbrite databaseQuery will be update automatically
databaseQuery
}
}
}
So at the end you create your SQLBrite Observable (QueryObservable) and pass it into the createDataAwareObservable() function. Than it will ensure that it loads the data from network if no data is here, otherwise it will check if the data should be updated in background (will save it into database, which then will update the SQLBrite QueryObservable automatically) or if the data is up to date.
Basically you can use it like this:
createDataAwareObservable( db.getAllDomains() ).subscribe(...)
So for you as user of this createDataAwareObservable() you always get the same type Observable<T> back as you pass in as parameter. So essentially it seems that you were always subscribing to db.getAllDomains() ...
if your problem is that you have to subscribe your observer every time that you want to get data you can use relay, which never unsubscribe the observers because does not implement onComplete
/**
* Relay is just an observable which subscribe an observer, but it wont unsubscribe once emit the items. So the pipeline keep open
* It should return 1,2,3,4,5 for first observer and just 3, 4, 5 fot the second observer since default relay emit last emitted item,
* and all the next items passed to the pipeline.
*/
#Test
public void testRelay() throws InterruptedException {
BehaviorRelay<String> relay = BehaviorRelay.create("default");
relay.subscribe(result -> System.out.println("Observer1:" + result));
relay.call("1");
relay.call("2");
relay.call("3");
relay.subscribe(result -> System.out.println("Observer2:" + result));
relay.call("4");
relay.call("5");
}
Another examples here https://github.com/politrons/reactive/blob/master/src/test/java/rx/relay/Relay.java
I cant find a way to combine or chain a list of observables that it´s responses are prerequisites to other call that creates another Observable.
I´m using retrofit with observables.
My Service:
String url = "/geocode/json?sensor=false";
#GET(url)
Observable<GeocodeResult> getReverse(#Query("key") String gMapsKey,
#Query("latlng") LatLng origin);
And another service needs that GeocodeResult
#POST("/api/orders")
Observable<Order> createOrder(#Body GeocodeResult newOrder);
And I´m trying with:
// Prerequisite 1
Observable geocodeObservable = Address.get(...);
// Call createOrder after geocode is obtained?
return Observable.combineLatest(geocodeObservable, geocode -> createOrder(geocode));
But it don´t work because combineLatest needs an object, not an observable but I need to return the observable.
With JoinObservable:
Pattern5<Geocode> pattern = JoinObservable.from(geocodeObservable)
Plan0<Observable<Order>> plan = pattern.then(Order::create);
return JoinObservable.when(plan).toObservable().toBlocking().single();
But it throws an NoSuchElementException exception. Why?
I do toBlocking().single() because I need the Observable and not the Observable<Observable<Order>> :(.
Or how can I do it?
You could try using flatMap which can take the second observable as an parameter.
The function takes the items emitted by the first observable and creates an observable for each of those items and then flattens the items emitted by those observables into a single observable. This sounds complex, but fortunately both your Retrofit functions emit only a single item, so only one observable gets "flattened" into a observable.
You can use flatMap like this:
restApi.getReverse(gMapsKey, origin)
.flatMap(geocodeResult -> createOrder(geocodeResult))
.subscribe(order -> doSomething(order));
combineLatest doesn't really fit your needs, because it would perform both REST calls at the same time, not one after the other, so you can't use the response of the first one as the parameter of the second. I can't comment on why the exception gets thrown for JoinObservable because it's not a part of any public API. Also toBlocking() shouldn't really be used for anything other than testing.
I ended up creating a new Object and using Observable.combineLatest to combine all the prerequisites creating a new Observable and then using flatMap to create the new Observable from that observable.
Observable<NewOrderWrapper> newOrderObservable = Observable.combineLatest(prerequisites, (param1, param2,...) -> {return new NewOrderWrapper(param1, param2,...)});
and then
Observable<Order> finalOrderObservable = newOrderObservable.flatMap(newOrderWrapper -> create(newOrderWrapper))
Check a post here MakinGIANST/RXJava post.
Thanks to #LukaCiko