To say that I have some code like this
Observable.concat(scores)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe({...});
Due to the instability on the server side, an onError() notification (eg error code 500) may be poped out when emitting one of the Observables and the concat() operator will be stopped from emitting the rest of Observables as noted in the doc.
So, the failed Observable needs to be emitting again as well as the rest of Observables.
From my point of view, I try to use toBlocking() operator
to turn the sequence of Observables into a blocking Observable and forEach() it
List<Observable> list = createListOfScores();
Observable.from(list)
.toBlocking()
.forEach(new Action1<Observable>() {
#Override
public void call(Observable observable) {
observable.subscribe(...).onErrorResumeNext(...)
}
});
There will be better solution than this. I hope someone can enlighten me.
Another way could to use retry method if onError is called for some Observable. However, in this case, Observables that run without any errors would have to be removed from the list so that they're not run again. Here's an example (here I retry to run a task 10 times before giving up):
#RunWith(RobolectricTestRunner.class)
#Config(manifest = Config.NONE)
public class RxTest {
#Test
public void testRetryObservableConcat() throws Exception {
final List<Observable<String>> observables = new CopyOnWriteArrayList<>(getObservables());
Observable.concat(observables)
//remove the observable if it's successful
.doOnNext(result -> observables.remove(0))
.doOnError(error -> System.out.println(error.getMessage()))
//if an error is thrown then retry the task only for this Observable
.retry(10, error -> true)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> System.out.println(result),
error -> System.out.println("onError called!"));
Thread.sleep(1000);
ShadowLooper.runUiThreadTasks();
}
private List<Observable<String>> getObservables() {
List<Observable<String>> observables = new ArrayList<>();
final Random random = new Random();
final AtomicInteger atomicInteger = new AtomicInteger(1);
for (int i = 0; i < 3; i++) {
final int id = i;
observables.add(Observable.fromCallable(() -> {
//for second Observable there's a 2/3 probability that it won't succeed
if (id == 1 && random.nextInt() % 3 != 0) {
throw new RuntimeException("Observable " + id + " failed!");
}
return "Observable #" + id + " returns " + atomicInteger.getAndIncrement();
}));
}
return observables;
}
}
Output
Observable 1 failed!
Observable 1 failed!
Observable 1 failed!
Observable #0 returns 1
Observable #1 returns 2
Observable #2 returns 3
As it can be seen from the example, after the second Observable finally succeeds each result is delivered in order.
Related
I have a scenario where I want to call same API for multiple devices and display result after completing all requests.
I am using retrofit 2.
I know little bit about RxJava. I thought zip operator will be suitable for this. So implemented as below.
API in ApiInterface :
#PUT(AppConstants.BASE_URL + AppConstants.PATH_SEPARATOR + "/user/endpoint")
Observable<ResponseBody> updateInfo(#Header("Authorization") String token, #Query("device_id") String deviceId, #Body JsonObject body);
Here is a method which calls API. It gets device id and its body in Map. This method calls API for every device id available in Map.
public void updateAllInfo(final HashMap<String, String> deviceIdMap, final ApiResponseListener listener) {
List<Observable<ResponseBody>> requests = new ArrayList<>();
ArrayList<String> reqIdList = new ArrayList<>();
for (Map.Entry<String, String> entry : map.entrySet()) {
String deviceId = entry.getKey();
String jsonBodyStr = entry.getValue();
Gson gson = new Gson();
JsonObject jsonBody = gson.fromJson(jsonBodyStr, JsonObject.class);
reqIdList.add(deviceId);
requests.add(apiInterface.updateSchedules("accessToken", deviceId, jsonBody));
}
Observable.zip(requests, new Function<Object[], List<ResponseBody>>() {
#Override
public List<ResponseBody> apply(Object[] objects) throws Exception {
Log.e("onSubscribe", "apply : " + objects.length);
List<ResponseBody> dataResponses = new ArrayList<>();
for (Object o : objects) {
dataResponses.add((ResponseBody) o);
}
return dataResponses;
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<List<ResponseBody>>() {
#Override
public void accept(List<ResponseBody> responseBodies) throws Exception {
Log.e("onSubscribe", "YOUR DATA IS HERE: " + responseBodies.size());
for (int i = 0; i < responseBodies.size(); i++) {
Log.e(TAG, "Response received for " + i + " is : " + responseBodies.get(i).string());
}
}
}, new Consumer<Throwable>() {
#Override
public void accept(Throwable throwable) throws Exception {
Log.e("onSubscribe", "Throwable: " + throwable);
}
});
}
I want to get the response (success / failure) for every device id. Means I need response and also id for which API is called.
Using zip operator, if any API is failed, failure is received in accept(Throwable throwable) method. If any API is failed, I think zip operator is not calling next API.
How can I get response (success or failure) for all request ?
Also need something to indicate the response is for which req / device id (Some mapping) to display result.
Is there any other operator I can use instead of zip ?
Any suggestion / help ?
I am a bit rusty in java, so I will write my answer in Kotlin, it should not be a problem for you to convert it yourself.
Create a helper class that will include the ResponseBody alongside with the deviceId:
data class IdentifiedResponseBody(
val deviceId: String,
val responseBody: ResponseBody?
)
Then:
// change the signature of your requests list to return IdentifiedResponseBody observables
val requests = mutableListOf<Observable<IdentifiedResponseBody>>()
...
// your stated API have updateInfo instead of updateSchedules, but I will assume they have the same signature
requests.add(
apiInterface.updateSchedules("accessToken", deviceId, jsonBody)
.map { responseBody ->
// map the added observable to return IdentifiedResponseBody
IdentifiedResponseBody(deviceId, responseBody)
}
.onErrorReturn { error ->
// return an item here instead of throwing error, so that the other observables will still execute
IdentifiedResponseBody(deviceId, null)
}
)
Finally, use merge instead of zip:
Observable.merge(requests)
.subscribeOn(Schedulers.io())
// it's a question if you want to observe these on main thread, depends on context of your application
.subscribe(
{ identifiedResponse ->
// here you get both the deviceId and the responseBody
Log.d("RESPNOSE", "deviceId=${identifiedResponse.deviceId}, body=${identifiedResponse.responseBody}")
if (responseBody == null || responseBody.hasError()) {
// request for this deviceId failed, handle it
}
},
{ error ->
Log.e("onSubscribe", "Throwable: " + error)
}
)
See merge: http://reactivex.io/documentation/operators/merge.html
See zip: http://reactivex.io/documentation/operators/zip.html
You should see the profound difference: zip combines your responses to one single item defined by your mapping function (i.e. list of responses in your case), while merge emits all responses individually, at the time they are returned. In case of zip here, the combined result is returned at the moment (and only) when all the requests have finished; you may not want this behavior, as if a single request would not return a response, you would not get any response at all.
UPDATE
The java equivalent should be as follow, but revise before trying out, as I am not sure if I converted everything correctly:
requests.add(
apiInterface.updateSchedules("accessToken", deviceId, jsonBody)
.map(new Function<ResponseBody, IdentifiedResponseBody>() {
#Override
public IdentifiedResponseBody apply(ResponseBody responseBody) throws Exception {
return new IdentifiedResponseBody(deviceId, responseBody);
}
})
.onErrorReturn(new Function<Throwable, IdentifiedResponseBody>() {
#Override
public IdentifiedResponseBody apply(Throwable throwable) throws Exception {
return new IdentifiedResponseBody(deviceId, null);
}
})
);
Observable.merge(requests)
.subscribeOn(Schedulers.io())
.subscribe(new Consumer<IdentifiedResponseBody>() {
#Override
public void accept(IdentifiedResponseBody identifiedResponseBody) throws Exception {
// same logic as from kotlin part
}
},
new Consumer<Throwable>() {
#Override
public void accept(Throwable throwable) throws Exception {
Log.e("onSubscribe", "Throwable: " + throwable);
}
});
UPDATE 2
In the comment you asked:
Is there any way from which I can get final callback for all requests
completed
That's the problem with using Observable instead of Single/Completable, it just does not finish unless you explicitly close the channel or an error is thrown. In ideal context, Observable should be used for streams that continuously emits some data, for example an open channel to Room DB, as there are no telling how many time the DB will change. I admit that in your case it seems to be difficult to apply something else than Observable. There is however a workaround:
Observable.merge(requests)
// emits only this much items, then close this channel
.take(requests.size.toLong())
// executed when the channel is closed or disposed
.doFinally {
// todo: finalCallback
}
.subscribeOn(Schedulers.io())
.subscribe(...)
The code is again in Kotlin, should not be hard to transform to java. Please check what Observable.take() does: http://reactivex.io/documentation/operators/take.html
I have a method to ping all computers on a list called computerItemList and check if it's online or not. I want to do it by sending parallel requests to API. Code below simulates response from server and it is working but I need information when all requests finish unblocking ping button. How to do that?
Generally, I want to execute async command on all items on list and then wait until all finish.
public void pingComputers() {
for (ComputerItem computerItem : _computerItemList) {
Observable.just(computerItem)
.subscribeOn(Schedulers.io())
.subscribe(item -> {
int sleepTime = randomizer.nextInt(1000) + 200;
int status = randomizer.nextInt(ComputerStatus.values().length - 1) + 1;
Log.d(TAG, Thread.currentThread().getName() + " >>pingSelected: " + item + " sleep: " + sleepTime);
Thread.sleep(sleepTime);
item.setStatus(ComputerStatus.values()[status]);
updateComputerList();
});
}
}
UPDATE
I wrote something like this. Is it okay?
public void ping2() {
Observable.fromIterable(_computerItemList)
.flatMap(computerItem -> {
return Observable.just(computerItem)
.subscribeOn(Schedulers.io())
.map(item -> {
int sleepTime = randomizer.nextInt(1000) + 200;
Log.d(TAG, "ping2: Sleeping for " + sleepTime);
Thread.sleep(sleepTime);
int status = randomizer.nextInt(ComputerStatus.values().length - 1) + 1;
item.setStatus(ComputerStatus.values()[status]);
updateComputerList();
return item;
});
}).doOnComplete(() -> Log.d(TAG, "ping2: COMPLETE")).subscribe();
}
UPDATE - IT IS WORKING! But is it okay???
public void executeCommand(CommandType command) {
isWorking.setValue(true);
Observable.fromIterable(_computerItemList)
.subscribeOn(Schedulers.io())
.flatMap(computerItem -> createPingObservable(computerItem))
.observeOn(AndroidSchedulers.mainThread())
.doOnComplete(() -> isWorking.setValue(false))
.subscribe();
}
private Observable<ComputerItem> createPingObservable(ComputerItem computerItem) {
return Observable.just(computerItem)
.subscribeOn(Schedulers.io())
.map(item -> {
int sleepTime = randomizer.nextInt(1000) + 200;
int status = randomizer.nextInt(ComputerStatus.values().length - 1) + 1;
Log.d(TAG, Thread.currentThread().getName() + ">>> executeCommand: PING");
Thread.sleep(sleepTime);
item.setStatus(ComputerStatus.values()[status]);
updateComputerList();
return item;
});
}
UPDATE - IT IS WORKING! But is it okay???
No, it's not ok to call updateComputerList(); that way, you would have to synchronized it because you will be calling it from multiple threads, each of them trying to mutate the state.
I would do it this way:
Observable.fromIterable(list)
.flatMap(computerItem -> createPingObservable(computerItem))
.observeOn(AndroidSchedulers.mainThread())
.subscribe(computerItem -> updateComputerList(computerItem));
}
Also, don't use Thread.sleep(sleepTime);, just use
.delay(randomizer.nextInt(1000) + 200, TimeUnit.MILLISECONDS)
My app is making repeat API requests to download chunks of data. Unfortunately the server limits the API requests to 3 per second. In the code below, how can I rate limit the requests to X per second ?
private void getHistoricalPrices(String currency, String start_date, String end_date, int granularity){
// show download status
currentDlPageIndex++;
view.showDownloadingStatus(currentDlPageIndex, totalDlPages);
// make the API call
addDisposable(
model.isNetworkAvailable()
.doOnSuccess(isNetworkAvailable -> {
if (!isNetworkAvailable) {
showErrorMessage();
Timber.v("no internet");
}
})
.filter(isNetworkAvailable -> true)
.flatMapSingle(isNetworkAvailable -> model.getHistoricalPrices(currency, start_date, end_date, String.valueOf(granularity)))
.subscribeOn(rxSchedulers.io())
.observeOn(rxSchedulers.mainThread())
.subscribe((Response<List<List<String>>> responseData) -> {
if (responseData != null && responseData.code() == HTTP_OK) {
List<List<String>> response = responseData.body();
if (response != null) {
// create list from downloaded data
ArrayList<HistoricPrice> tmpHistoricPriceList = new ArrayList<>(response.size());
for (List<String> rawHistoricPrice : response) {
HistoricPrice historicPrice = new HistoricPrice(rawHistoricPrice.get(0), rawHistoricPrice.get(1), rawHistoricPrice.get(2), rawHistoricPrice.get(3), rawHistoricPrice.get(4), rawHistoricPrice.get(5));
tmpHistoricPriceList.add(0, historicPrice);
}
// add the downloaded list to the main list being recreated
model.historicPriceList.addAll(tmpHistoricPriceList);
Timber.d("added %d records to memory", response.size());
// if there's more to download, download another chunk
if (intermediateDateSecs != null && intermediateDateSecs < endDateSecs){
startDateSecs = tmpHistoricPriceList.get(tmpHistoricPriceList.size()-1).time + granularity;// add "granularity" to startDateSecs to avoid getting two exact data for the same time
Date startDate = new Date(startDateSecs * 1000L);//requires milliseconds, not epoch
String startStrDate = DateUtils.fromDateToString(startDate);
intermediateDateSecs = startDateSecs + ((ApiService.MAX_HISTORIC_RETURN_VALUES - 1) * granularity);
if (intermediateDateSecs > endDateSecs) intermediateDateSecs = endDateSecs;
Date intermediateDate = new Date(intermediateDateSecs * 1000L);
String intermediateStrDate = DateUtils.fromDateToString(intermediateDate);
getHistoricalPrices(currency, startStrDate, intermediateStrDate, granularity);
} else {
// no more to download, save data
Timber.d("downloaded total of %d records", model.historicPriceList.size());
view.hideDownloadingStatus();
showSaveDataMessage();
}
}
}
}, error -> {
showErrorMessage();
})
);
}
you can see that the method getHistoricalPrices() calls itself to continue downloading. This implementation works well, except for the server complaining when there's too many API requests per second.
You can perform your request every X milliseconds / seconds like so:
long interval = 0L; // Your desired interval here in milliseconds
Observable.interval(0, interval, TimeUnit.MILLISECONDS)
.takeUntil(x -> isTaskComplete == true)
// Other configuration calls go here
.subscribe(); // make request here or in doOnNext()
For my app I have to run two operations, both being asynchronous:
read from a file ( I use this file to simulate reading from a data bus ) - async operation because I don't know "when" arrive a new
message/character on the bus. I search for a specific sequence
character, eg frame start_bytes = "xx" and the 4 following bytes are
"the data" I wait for.
read / update data to Firebase, depending on the "data" read from file - async operation due to addValueEventListener use.
I'm thinking a semaphore/mutex mechanism or a simple boolean flag that one task signal to the other one that a new data must be saved/updated to Firebase.
How can I synchronize these two operations ( by embedding them in a Task / AsyncTask / Thread)?
I ran a search for these topics but I found examples related to UI, ProgressBars and so on .. not really suited/useful to my situation.
read / update data in Firebase
myRefDevices.addValueEventListener(new ValueEventListener() {
// addValueEventListener
// This method is called once with the initial value and again
// whenever data at this location is updated.
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
boolean bChildFound = false;
DatabaseReference dbrefChildFound;
final CDeviceStatus obj_new = new CDeviceStatus();
for( DataSnapshot val : dataSnapshot.getChildren() )
{
if( val.getKey().contentEquals(MAC_ADDRESS[ iIterator ]) )
{
bChildFound = true;
dbrefChildFound = val.getRef();
obj_new.setiAvailable_A( val.getValue( CDeviceStatus.class ).getiAvailable_A() + 1 );
obj_new.setsID(val.getValue( CDeviceStatus.class).getsID() );
dbrefChildFound.setValue(obj_new);
}
}
if(!bChildFound)
{
Log.d("child=" + MAC_ADDRESS[ iIterator ], "not found");
}
if(++iIterator == 16)
{
iIterator = 0;
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
read from file :
try {
// open input stream text file for reading
Resources res = getResources();
InputStream instream = res.openRawResource( R.raw.simulated_bus );
// we convert it to bufferred input stream
BufferedInputStream bistreamSimulatedBus = new BufferedInputStream(instream);
try{
// if we want to stop reading from the file / simulated bus for whatever reason..
boolean bStayInLoop = true;
while ((bistreamSimulatedBus.available() > 0) && bStayInLoop)
{
try {
// throw new InterruptedException();
char c = (char) bistreamSimulatedBus.read();
if( COUNT_CHARACTERS_NEWLINE )
{
if ( '\n' == c ){
// we can count how much NewLine character we have
//iNL_Counter++;
}
}
...
}
catch ( InterruptedException e ) {
throw new RuntimeException( e );
}
}
} catch (IOException e) {
throw new RuntimeException( e );
}
finally {
// release any resource associated with streams
if ( null != instream ) {
instream.close();
}
if ( null != bistreamSimulatedBus ) {
bistreamSimulatedBus.close();
}
}
}
catch (Exception e) {
throw new RuntimeException( e );
}
Thank you.
Let us break the solution like this:
The basics
You have two operations : o1 and o2. You want the second operation to execute as soon as the first one has completed.
It clearly appears to me that you need an event-driven solution here.
Approach
Using the concept of Publisher/Subscriber design pattern, you can make the Initiator of o1 be the Publisher of an event. Then, when this particular operation o1 is completed, let the class (activity, fragment, service) notify the other class which we will call Subscriber.
Code
Add the following line to your build.gradle (app-level):
compile 'org.greenrobot:eventbus:3.0.0'
Then, simply create a simple Plain Old Java Object (POJO) that represents your event.
public class RequestCompletedEvent{ // add constructor and anything you want}
Next, to Publish the event, you simply call the post(POJO instance) like this:
EventBus.getDefault().post(new RequestCompletedEvent(true));
Then, finally, in the Subscriber class, simply listen for notifications by adding the following lines of code:
#Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
#Override
public void onStop() {
super.onStop();
EventBus.getDefault().unregister(this);
}
Then still within the same class, use the Subscribe annotation to catch any signals:
#Subscribe
public void onEvent(RequestCompletedEvent event) {
/* Do something */
//trigger the second operation here;
startOperationTwo();
}
Summary
It would help to note here that the easiest way to pull this off is to use an async task (AsyncTask sub class) to read your files, then when successfully done, inside onPostExecute(), you can notify the Subscriber to initiate the next operation.
I hope this helps; and good luck! Let me know if you need further assistance!
I'm just starting to learn RxJava and I've read and watched quite a few tutorials, but some things are not clicking just yet. I've dived in from the deep end and started by modifying one of my app's API calls to return an Observable. Currently the API call is used by an AsyncTaskLoader which returns cached data from a local database, then does the call, merges the data and returns it again. This pattern sounded like a perfect subject for my RxJava experiments, but starting small, I want to return an observable from my API call.
This is my original call:
public static ArrayList<Stuff> getStuffForId(String id)
throws IOException, UserNotAuthenticatedException {
Profile profile = getProfile();
HashMap<String,ArrayList<Stuff>> map = profile.getStuff();
if (map == null) {
throw new IOException("error processing - map cannot be null");
}
return map.get(id);
}
private static Profile getProfile()
throws IOException, UserNotAuthenticatedException {
// <.. getting url, auth tokens and other stuff to prepare the Request ..>
Response response = sHttpClient.newCall(request).execute();
if (response.code() == ERR_AUTH_REJECTED) {
throw new UserNotAuthenticatedException(response.body().string());
}
if (!response.isSuccessful()) {
throw new IOException("Unexpected code " + response);
}
Gson gson = new Gson();
String result = response.body().string();
response.body().close();
return gson.fromJson(result, Profile.class);
}
And in RxJava world I'm thinking something along the lines of:
public static Observable<ArrayList<Stuff>> getStuffForId(String id) {
return getProfile().map(
Profile::getStuff).map(
map -> {
if (map == null) {
Observable.error(new IOException("error processing - map cannot be null"));
}
return map.get(id);
});
}
private static Observable<Profile> getProfile() {
return Observable.fromCallable(() -> {
// <.. getting url, auth tokens and other stuff to prepare the Request ..>
Response response = sHttpClient.newCall(request).execute();
if (response.code() == ERR_AUTH_REJECTED) {
throw new UserNotAuthenticatedException(response.body().string(),
authToken);
}
if (!response.isSuccessful()) {
throw new IOException("Unexpected code " + response);
}
Gson gson = new Gson();
String result = response.body().string();
response.body().close();
return gson.fromJson(result, Profile.class);
});
}
Does this look anything like what one would expect? I'm still not sure of the difference between fromCallable() and defer() and where you use one or another. Also not sure where do the exceptions go once thrown inside the fromCallable() method - will they automagically end up in onError of my subscriber or do I need to handle them in my getStuffForId method? And finally, Android Studio is warning me that return map.get(id) might throw a nullPointer. Is that just because the IDE doesn't understand that Observable.error will terminate the execution, or is it that I don't understand what will happen on Observable.error?
1) Difference b/w fromCallable() and defer()?
Well defer() does not create the Observable until some subscriber subscribes and it creates new Obervable each time the user subscribes. Refer this link for why you would want to use defer().
2) Where do exceptions go once thrown inside the fromCallable() method?
Exceptions are caught inside the Observer and then passed as a Throwable object to Subscriber's onError() method.
3) Android Studio is warning me that return map.get(id) might throw a nullPointer.
Its because when it is actually null you are not returning anything in the if statement. The code will run beyond if statement, thus causing nullPointerException. Observable.error()); returns an Observable and it doesn't throw anything, in order to do that you have to explicitly throw a RuntimeException.
4) Does this look anything like what one would expect.
Apart from the above error, there is nothing wrong but you can search online for the rxJava patterns to code better structurally.
Does this look anything like what one would expect
Yes, but please have a look at libraries, which will do the plumbing for you: https://github.com/square/retrofit
If you throw any exception in fromCallable you would use defer and return Observable.error(new Exception("error"))
Difference fromCallable / defer
Both methods are factories for creating observables. From callable asks you to return a value as in string etc. Defer like you to return a observable of something. You would use fromCallable if you would like to wrap some non-observable-method-call which returns a certain type. Furthermore fromCallable will handle exceptions for your and pass them down the chain. In contrast you would use defer if you would like to handle your own exception/ observable. You may return a observable which emits a value and then finishes or not finishes. 'fromCallable' will alsways finish with a value(s) and onComplete or onError. With defer you may produce a observable, which will never end as in Observable.never().
will they automagically end up in onError
Yes, exception will be caught and passed along as onError. You may handle the error with a operator right away in the chain or you may provide a onError handle (overload) on subscription.
return map.get(id) might throw a nullPointer
If you are using RxJava1.x you may encounter null values in the stream, because passing values with onNext(null) are valid. Therefor you would need a null check, due to the possibility of a NPE. Instead of the null-check you could filter-out null values with filter-operator as in:
Observable.just("1", "2", null, "3")
.filter(s -> s != null)
.map(s -> s.getBytes())
.subscribe(bytes -> {
//...
});
In this case you will get a warning about a possible NPE at s.getBytes(). But due to the filter-operation you can be sure, that s is never null.