Please look at this simple code:
private void refreshData(String endpoint)
{
KProgressHUD busy = Utils.showBusyIndicator(MainActivity.this);
MCityEndpoint mcityService = ServiceFactory.createRetrofitService(MCityEndpoint.class, Configuration.getApiUrl());
Disposable disposable = Observable.zip
(
mcityService.getMcityDictionaries(selectedCity.id),
mcityService.getEvents(selectedCity.id, endpoint),
mcityService.getUserDetails(selectedCity.id),
(dictionaries,events, userInfo) ->
{
processData(dictionaries, events, userInfo);
return events.events;
}
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result ->
{
if(mFragment instanceof ListFragment)
{
((ListFragment) mFragment).refreshData(result);
}
updateUserDetails();
busy.dismiss();
Log.d(Configuration.tag,"Refresh data complete");
}, throwable ->
{
Log.d(Configuration.tag,throwable.toString());
busy.dismiss();
this.logout(true);
});
mCompositeDisposable.add(disposable);
}
I want to call 3 observables, process their values and finally load data to list. It works, but I'm getting 1 second lag. Result is:
Busy indicator shows up
Busy indicator hides
1 second lag without busy indicator visible
List is refreshed after lag
I can't understand reason of lag in this case. Everything should be refreshed with busy indicator visible. Do you have any idea?
Related
I'm adapting some sample code from what3words for accessing their API via their Java SDK. It uses RXJava.
The sample code is:
Observable.fromCallable(() -> wrapper.convertTo3wa(new Coordinates(51.2423, -0.12423)).execute())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> {
if (result.isSuccessful()) {
Log.i("MainActivity", String.format("3 word address: %s", result.getWords()));
} else {
Log.e("MainActivity", result.getError().getMessage());
}
});
First of all. this gives a deprecation warning when building and a IDE warning (Result of 'Observable.subscribe()' is ignored).
To resolve this first issue I have added Disposable myDisposable = in front of the Observable. Is this correct? (See below for where it is added)
Next I need to add a timeout so that I can show a warning etc if the request times out. To do this I have added .timeout(5000, TimeUnit.MILLISECONDS) to the builder.
This works, but the way timeouts seem to work on Observables is that they throw an exception and I cannot figure out how to catch and handle that exception.
What I have right now is:
Disposable myDisposable = Observable.fromCallable(() -> wrapper.convertTo3wa(new Coordinates(51.2423, -0.12423)).execute())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.timeout(5000, TimeUnit.MILLISECONDS)
.subscribe(result -> {
if (result.isSuccessful()) {
Log.i("MainActivity", String.format("3 word address: %s", result.getWords()));
} else {
Log.e("MainActivity", result.getError().getMessage());
}
});
This builds and runs fine, and the API/deprecation warning is not shown, BUT when no network is available this correctly times out and throws the unhandled exception.
So, the code seems to be correct, but how on earth do add the exception handling to catch the timeout TimeoutException that is thrown?
I've tried numerous things, including: adding a try-catch clause around the whole Observable - this warns that TimeoutException is not thrown by the code in the `try; and adding an error handler.
Adding the error handler has got me closest, and so the code below is as far as I have got:
Disposable myDisposable = Observable.fromCallable(() -> wrapper.convertTo3wa(new Coordinates(51.2423, -0.12423)).execute())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.timeout(5000, TimeUnit.MILLISECONDS)
.subscribe(result -> {
if (result.isSuccessful()) {
Log.i("MainActivity", String.format("3 word address: %s", result.getWords()));
} else {
Log.e("MainActivity", result.getError().getMessage());
}
}, error -> {
runOnUiThread(new Runnable() {
#Override
public void run() {
myTextView.setText(R.string.network_not_available);
}
});
});
This catches the Timeout correctly and updates my UI without error, however when the network is restored it seems that the Observable might be trying to return and a null pointer exception is thrown.
(Update, this NPE might actually be being thrown sometimes after a short time whether the network is restored or not... but it is always thrown when the network restores.)
I get FATAL EXCEPTION: RxCachedThreadScheduler-1 and java.lang.NullPointerException: Callable returned a null value. Null values are generally not allowed in 3.x operators and sources.
Do I need to destroy the Observable or something to prevent the NPE?
You need to add an onError handler to your subscribe call:
.subscribe(result -> {
if (result.isSuccessful()) {
Log.i("MainActivity", String.format("3 word address: %s", result.getWords()));
} else {
Log.e("MainActivity", result.getError().getMessage());
}
},
error -> {
// handle error here
});
When a an exception makes it to a subscribe call that does not have an onError handler, it will throw a OnErrorNotImplementedException, like this:
io.reactivex.exceptions.OnErrorNotImplementedException: The exception was not handled due to missing onError handler in the subscribe() method call. Further reading: https://github.com/ReactiveX/RxJava/wiki/Error-Handling | java.util.concurrent.TimeoutException: The source did not signal an event for 1 seconds and has been terminated.
Adding the onError handler will prevent that, and the onError handler will get called instead.
There's a few things going on here:
First of all. this gives a deprecation warning when building and a IDE warning (Result of 'Observable.subscribe()' is ignored).
subscribe() returns a Disposable. The idea is that when you're no longer interested in receiving the output of your observable, you call dispose() on the disposable and the work terminates. This can also prevent memory leaks.
As an example, imagine you have an Activity, and you start an Observable to run a long network query which finally posts something to the Activity UI. If the user navigates away before this task completes, or the Activity is otherwise destroyed, then you're no longer interested in its output because there is no longer a UI to post to. So you may call dispose() in onStop().
So, the code seems to be correct, but how on earth do add the exception handling to catch the timeout TimeoutException that is thrown?
Using the error block in subscribe is one option, but there are others. For example, if you wanted to keep using your Result class, you could use something like onErrorReturn(throwable -> Result.error(throwable)). Obviously I'm guessing what that class looks like:
.timeout(5000, TimeUnit.MILLISECONDS)
.onErrorReturn(throwable -> Result.errorWithMessage(R.string.network_not_available))
.subscribe(result -> {
if (result.isSuccessful()) {
Log.i("MainActivity", String.format("3 word address: %s", result.getWords()));
} else {
myTextView.setText(result.getErrorMessage());
}
});
java.lang.NullPointerException: Callable returned a null value. Null values are generally not allowed in 3.x operators and sources.
This:
wrapper.convertTo3wa(new Coordinates(51.2423, -0.12423)).execute()
is returning null. You can do something like:
Observable.fromCallable(() -> {
Result<?> out = wrapper.convertTo3wa(new Coordinates(51.2423, -0.12423)).execute();
if(out == null)
out = Result.error(/*Returned null*/);
}
return out;
}
I'm currently trying to learn RxJava in Android. I require some guides.
At the moment, I'm trying to rewrite AsyncTask below to RxJava:
public class MyAsyncTask extends AsyncTask<Void, ProgressInfo, Result> {
#Override
protected Result doInBackground(Void... void) {
//Long running task
publishProgress(progressInfo);
//Long running task
return result;
}
#Override
protected void onProgressUpdate(ProgressInfo... progressInfo) {
//Update the progress to UI using data from ProgressInfo
}
#Override
protected void onPostExecute(Result res) {
//Task is completed with a Result
}
}
In AsyncTask approach shown above, I can update the UI about the progress by making use of onProgressUpdate method, I pack every data I needed into ProgressInfo and reflect the UI in onProgressUpdate. After task ends, the Result will be passed from from doInBackground to onPostExecute.
But, when I'm trying to implement this with RxJava, I have a hard time dealing with it. Since I cannot pass any parameter to onComplete in Observer. And thus, I ended up with following implementation. I merged the pass of the ProgressInfo and Result into onNext.
Observable.create(emitter -> {
//Long running task
emitter.onNext(progressInfo);
//Long running task
emitter.onNext(result);
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object -> {
if(object instanceof ProgressInfo){
//Update the progress to UI using data from ProgressInfo
}else if(object instanceof Result){
//Task is completed with a Result
}
});
QUESTION 1: Is my implementation/concept in RxJava right or wrong?
Although it works, I personally feels the implementation above strange and wrong to me. Since the task ultimately is just trying to do some calculations and come out with a single item - Result. The emission of ProgressInfo is like a "side" thing but not "main" thing. I should implement it with Single.create(). But if I did this, I cannot think of any way to pass any ProgressInfo to my UI.
QUESTION 2:
Is there a better idea/way to emit single item while updating the UI during the process?
If yes, how would you implement this logic in RxJava? Can you show me your codes/examples?
QUESTION 1: Is my implementation/concept in RxJava right or wrong?
Surely it depends on your use-case. If you want to provide feedback on each progress-step, there is no way, which I am aware of, to do it differently. I would recommand to provide progress feedback, when the task takes quite a few time and you are able to provide meaningful progress-information.
Either use a union of ProgressInfo and Result in one type and test for null or use a marker interface, from which ProgressInfo and Result inherite from.
interface ResultT { }
final class ProgressInfo implements ResultT { }
final class Result implements ResultT { }
When the result is emitted via onNext, I would recommand to complete the observable, in order to give notice to the subscriber, that the task has been done. The subscriber will receive the result via onNext and a onComplete afterwards.
Observable.<ResultT>create(emitter -> {
emitter.onNext(progressInfo);
emitter.onNext(result);
emitter.onComplete();
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object -> {
if (object instanceof ProgressInfo) {
//Update the progress to UI using data from ProgressInfo
} else if (object instanceof Result) {
//Task is completed with a Result
}
});
If you have no meaningfull progress-information, I would recommend using a Single.
QUESTION 2: Is there a better idea/way to emit single item while updating the UI during the process?
The doOn*-Operators could be used, to update the UI on subscription and termination. This way is one of the easiest, but could cause problems, when events from other subscriptions interleave with UI changes^1
.doOnSubscribe(disposable -> {/* update ui */})
.subscribe(s -> {
// success: update ui
},
throwable -> {
// error happened: update ui
},
() -> {
// complete: update ui
});
My recommandation would be modelling all States (e.g. Success/ Error) via a class and switch-case in the the subscribe-method (see ^1). First emit an StartProgress-event, then the ProgressInformation ones and on finish the SucessResult. Catch any errors with onError*-operators and return a FailureResult, which contains a error-message and maybe the throwable.
Observable.<ResultT>create(emitter -> {
emitter.onNext(progressInfo);
emitter.onNext(result);
emitter.onComplete();
}).startWith(new StartProgress())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.onErrorReturn(throwable -> new FailureResult(throwable))
.subscribe(object -> {
// when StartProgress -> updateUI
// when ProgressInformation -> updateUI
// ...
});
^1 http://hannesdorfmann.com/android/mosby3-mvi-1
1- Create a data class called ProgressInfo
data class ProgressInfo(val progress: Float,val downloadedFile: File, val isCompleted: Boolean = false )
2- Create observable
Observable.create<ProgressInfo> { emitter ->
try {
val url = URL("mediaUrl")
val targetFile = File( "filePath")
if (targetFile.exists().not() && targetFile.createNewFile()) {
val openConnection = url.openConnection()
openConnection.connect()
val totalBytes = openConnection.contentLength
val openStream = openConnection.inputStream
var downloadedBytes = 0f
openStream.use { inStream ->
FileOutputStream(targetFile).use { outStream ->
val streamSlice = ByteArray(1024)
while (true) {
val read = inStream.read(streamSlice)
if (read == -1) {
// file download complete
val progressInfo =
ProgressInfo(
(downloadedBytes / totalBytes) * 100f,
targetFile,
true
)
emitter.onNext(progressInfo)
break
}
downloadedBytes += read
outStream.write(streamSlice)
// update progress
emitter.onNext(
ProgressInfo(
(downloadedBytes / totalBytes) * 100f,
targetFile
)
)
}
}
}
}
emitter.onComplete()
} catch (ex: Exception) {
emitter.onError(ex)
}
}.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
// update your progress here
}, {
// on error
},{
// on complete
})
I have the following code, which returns a lot of data in the response, so much that I get a NullPointerException when it's being loaded in the android Activity when I scroll down too fast (since not all the data has been initialized yet), no problems if I wait a second and then scroll.
I want a way to delay the subscribe part, so that the Response<GetFeedTopicsResponseBody> is entirely populated with data (none is not initialized) when I call setAdapter. I tried checking response.isSuccessful but that does not work because no problem with the response itself, just the data takes time to deserialize into Java objects from JSON. I also tried onComplete in subscribe but that does not work either.
So I want either a way in RxJava2 to have a boolean value switch to notify the following subscription once it is complete, it will subscribe.
mGetFeedTopicsDisposable = ApiClient.getInstance()
.doGetFeedTopicsQuery(request)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
.subscribe((Response<GetFeedTopicsResponseBody> response) -> {
if (response.body() != null) {
List<Topic> topics = response.body().getTopics();
if (topics != null) {
mBinding.fragmentTopicListRecyclerTopics.setAdapter(TopicListAdapter.getInstance(topics));
if (response.body().isPaginated()) {
mRequestBuilder.setCursor(response.body().getCursor());
}
}
}
}, (Throwable ex) -> {
Log.e(TAG, ex.getMessage());
});
The error message I specifically got was:
java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String com.models.User.getThumbnailImageUrl()' on a null object reference
where this User object is set as a field of the Topic object which is added into the list of topics retrieved with getTopics(). If I don't scroll, I don't get this NullPointerException and the thumbnail urls for the Users are loaded properly.
Question : How do I delay RxJava2 Subscription?
Example :
I have added repeat(...) for better understanding.
io.reactivex.Observable
.just(new Object())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.delay(10, TimeUnit.SECONDS)
.repeat(2)
.doOnSubscribe(disposable -> Log.d("Delay Example ","Observer subscribed at : "+ DateFormat.getDateTimeInstance().format(new Date()) + " and execute after 10 seconds"))
.subscribe(new DefaultObserver<Object>() {
#Override
public void onNext(Object o) {
Log.d("Delay Example ","on Next : "+ DateFormat.getDateTimeInstance().format(new Date()));
}
#Override
public void onError(Throwable e) {}
#Override
public void onComplete() {
Log.d("Delay Example ","on Complete : "+ DateFormat.getDateTimeInstance().format(new Date()));
}
});
Output:
You see, on Next is called twice with 10 second delay.
Here, you can do adapter related operations in onComplete. :)
Hope this answers the question that you've asked.
I have a Retrofit call and want to recall it every 30sec. To do that I use an Observable.interval(0, 30, TimeUnit.SECONDS)
Observable
.interval(0, 30, TimeUnit.SECONDS)
.flatMap(x -> RestApi.instance().getUsers())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(list -> {
// ...
},
error -> Timber.e(error, "can't load users"));
My problem: If the api call fails, onError is called and the subscription unsubscribes and the polling isn't working anymore :-(
To catch the api error I added a retryWhen
Observable
.interval(0, 30, TimeUnit.SECONDS)
.flatMap(x -> RestApi.instance().getUsers()
.retryWhen(errors -> errors
.flatMap(error -> Observable.timer(15, TimeUnit.SECONDS))))
.observeOn(AndroidSchedulers.mainThread())
.subscribe(list -> {
// ...
},
error -> Timber.e(error, "can't load users"));
This catches the error but I get multiple api calls over the time. Every 30sec I get a new poll signal which ends in a new api request. But if the api request fails it retries itself. So I have a new request plus all retries.
My question: How can I handle an api error without unsubscribing from the poll signal?
Read how to properly use retryWhen and repeatWhen.
http://blog.danlew.net/2016/01/25/rxjavas-repeatwhen-and-retrywhen-explained/
And how to use onError operators:
http://blog.danlew.net/2015/12/08/error-handling-in-rxjava/
It's really easy w Rx :) I'm not gonna give you a final solution, just play around with it and try to understand the flow here.
If you want your getUsers request to not end up in onError when a request fails, instead of returning Observable<yourUserType> getUsers(), make it return an Observable<Response<yourUserType>> getUsers(). This way you'll be able to intercept the network error in the Response object.
This method works only if you are using retrofit 2.x
You can use onErrorResumeNext or onExceptionResumeNext and pass there "error" value.
You can look for other error handlings depends on your needs here
You can use this code, in this code implement the numbers attempts and time delay between request
private static int COUNTER_START = 1;
private static final int ATTEMPTS = 6;
private static final int ORIGINAL_DELAY_IN_SECONDS = 2;
remoteData.getAllRides(idSearch)
.repeatWhen(new Func1<Observable<? extends Void>, Observable<?>>() {
#Override
public Observable<?> call(Observable<? extends Void> observable) {
return observable.flatMap(new Func1<Void, Observable<?>>() {
#Override
public Observable<?> call(Void aVoid) {
if(COUNTER_START > ATTEMPTS){
throw new RuntimeException();
}
COUNTER_START++;
return Observable.timer(ORIGINAL_DELAY_IN_SECONDS, TimeUnit.SECONDS);
}
});
}
})
.takeUntil(new Func1<RideResponse, Boolean>() {
#Override
public Boolean call(RideResponse rideResponse) {
return rideResponse.getState().equals("finished");//this is the validation finish polling
}
}).filter(new Func1<RideResponse, Boolean>() {
#Override
public Boolean call(RideResponse rideResponse) {
return rideResponse.getState().equals("finished"); //this is the validation finish polling
}
}).map(rideResponse -> Log.e("",rideResponse.toString()))
.doOnError(err -> Log.e("Polling", "Error retrieving messages: " + err));
I'm using RxJava in and Android application with RxAndroid. I'm using mergeDelayError to combine two retro fit network calls into one observable which will process emitted items if either emits one and the error if either has one. This is not working and it is only firing off the onError action when either encounters an error. Now to test this I shifted to a very simple example and still the successAction is never called when I have an onError call. See example below.
Observable.mergeDelayError(
Observable.error(new RuntimeException()),
Observable.just("Hello")
)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.finallyDo(completeAction)
.subscribe(successAction, errorAction);
The success action will only be called if I use two success observables. Am I missing something with how mergeDelayError is supposed to work?
EDIT:
I've found that if I remove the observeOn and subscribeOn everything works as expected. I need to specify threads and thought that was the whole point of using Rx. Any idea why specifying those Schedulers would break the behavior?
Use .observeOn(AndroidSchedulers.mainThread(), true) instead of .observeOn(AndroidSchedulers.mainThread()
public final Observable<T> observeOn(Scheduler scheduler, boolean delayError) {
return observeOn(scheduler, delayError, RxRingBuffer.SIZE);
}
Above is the signature of observeOn function. Following code works.
Observable.mergeDelayError(
Observable.error(new RuntimeException()),
Observable.just("Hello")
)
.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) {
}
});
Got this trick from ConcatDelayError thread: https://github.com/ReactiveX/RxJava/issues/3908#issuecomment-217999009
This still seems like a bug in the mergeDelayError operator but I was able to get it working by duplicating the observerOn and Subscribe on for each observable.
Observable.mergeDelayError(
Observable.error(new RuntimeException())
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io()),
Observable.just("Hello")
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
)
.finallyDo(completeAction)
.subscribe(successAction, errorAction);
I think you don't wait for the terminal event and the main thread quits before the events are delivered to your observer. The following test passes for me with RxJava 1.0.14:
#Test
public void errorDelayed() {
TestSubscriber<Object> ts = TestSubscriber.create();
Observable.mergeDelayError(
Observable.error(new RuntimeException()),
Observable.just("Hello")
)
.subscribeOn(Schedulers.io()).subscribe(ts);
ts.awaitTerminalEvent();
ts.assertError(RuntimeException.class);
ts.assertValue("Hello");
}