fun remove(data: String): Single<JSONApiObject> {
return service.remove(data)
.onErrorResumeNext(ErrorHandler(ErrorParser()))
}
Is onErrorResumeNext necessary, if I don't intend to do anything onError? This is a POST request.
No. But it recommendable to implement the onError or onErrorResumeNext in order to handle whenever your subscription goes wrong. Otherwise, your program will crash.
For example, in your case, if your POST request fails you can make know your user that is caused by a network disconnection, fields missing or if the server is down.
Related
I am stuck on a problem and the situation is that I am calling Observable getFilms() and when the token has expired and it throws an onError I would like to ask another Observable for refresh token 'wait' and have this getFilms() method resubscribe.
I tried retryWhen but it is called 3 times which is not what I want and it doesn't give the desired effect even with setting the firstAttempt condition
getFilms() .hereWaitForRefreshTokenObservable() .~ .subscribe(films ->)
If anyone gets here the solution to this is to use Authenticator correctly.
In my case I doubted its performance and spent a couple of hours trying to fix it in the RXJava layer.
I simply forgot to add the "Bearer" :
.header("Authorization", "Bearer $newToken")
Observable 'wait' for another observable for access token and resubscribe
Answer to the question in the title:
getFilms()
.onErrorResumeNext { ex ->
if(ex is HttpException && ex.code() == 401){
reauthorize()
.flatMap {
getFilms()
}
} else {
Observable.error(ex)
}
}
That's a very common pattern when using expiring access, like JWT.
Side note: I assume we're talking about http calls and in such case you probably should use Maybe (eventually Single or Completable) but not Observable. Observable is for sequences returning more than 1 element and a http call usually can't return more than 1 response. Not unless you have some very advanced cache that common clients lack.
When I make a request to the network, if an error occurs, then I will return data from the cache and the error. But sometimes I don’t get data from the cache, but I get only an error. The first time I launch the application, I always get only an error. If I call the getDashboard method once or several times, then everything is fine.
Here is a piece of code.
.onErrorResumeNext(throwable -> {
return Observable.concat(Observable.just(fromCache), Observable.error(throwable));
});
Full code here
https://gist.github.com/githubgist123/7e027675bb4db07fef606e23f39f8a96
Sorry, but the way you're doing your caching is wrong in my opinion. If you request twice consecutively and the cache is empty you'll end up with two network requests running and racing each other to write to the cache. You don't want that.
What you need is:
Observable.concat(
cache(),
network().share().doOnNext(setCache(...)).onErrorReturn(...)
.first()
)
You might think of synchronized your cache too or make it thread-safe by using the proper data structure.
I have such case. User starts chain of request by clicking a button. Then he will make two photos uploading. However after starting uploading photos, he might return and start process over.
My CompositeDisposable() is attached to the viewModel it will only get be cleared after onCleared(). This is why strange issue occurs: user might start uploading photos, go back, start again and responses from old requests will be delivered, before new ones will be uploaded!
How should I modify all my regular RxJava requests and zip operator to look only after requests which are new, not old ones.
Again, I can't call CompositeDisposable.dispose(), before each button event, because that would terminate upload process.
I need to only dispose possible old responses.
Here is my sample:
//called two times, for uploading
fun uploadPhoto(){
compositeDisposable.add(
apiService.networkRequest(linkedHashMap, url)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(object: DisposableSingleObserver<retrofit2.Response<String>>() {
override fun onSuccess(t: retrofit2.Response<String>) {
// will provide result even if two new uploadPhoto() methods gets called
handleResponse(t)
}
override fun onError(e: Throwable) {
}
}))
}
}
fun handleResponse(response: retrofit2.Response<String>)
{
responseList.add(response) //saves number of responses
if(responseList.size == 2)
{
//calls new network request and creates a new logic.
}
}
The problem is that handleResponse() gets called, after uploadPhoto() returns previous result
OK, if I understood correctly your case, you want to discard the response from the first upload and take in consideration the response from the second, or to generalize: ignore any previous responses and take in consideration only the latest.
If this is so then one simple solution would be to check the compositeDisposable every time before starting a new upload. If the list is not empty then discard everything and add the new disposable to it.
Something like this:
fun uploadPhoto(){
if(compositeDisposable.size() > 0){
compositeDisposable.clear()
}
// ...
}
Pay attention to use compositeDisposable.clear() not .dispose().
Regarding your follow up question:
So, calling compositeDisposable.clear() will dispose each and every item in the list, more specifically this means that the working thread will be interrupted and, yes, in your case it means the upload process will be terminated.
If you want the upload to continue, then you will have to come up with a different mechanism other than clearing the disposables.
I am not sure if you could do that in Rx, but one idea not involving Rx would be to have some kind of uploadId, like a random hash generated, associated with each upload. This id should be given to your networking layer and then passed back in the response.
Then in the ViewModel you would keep track of the currentUploadId, and:
whenever the user performs a new upload, update currentUploadId with new generated id
whenever handleResponse(...) is received, you
check response.uploadId with currentUploadId, if they don't
match then you simply discard this response.
I have the following requirement:
Multiple observers (fragments) need to subscribe to a data source.
Activity will start a network request. Once the request is successful, each observer will receive the result.
I've trying to do it using cache / publish operators, but the issue is when initial request returns an error. At this point I wish to reset the stream and subsequent calls to the method should run a new network request instead of returning an error each time.
Here's what I have currently.
private Flowable<List<Data>> dataObservable;
private Flowable<List<Data>> getData(){
if(dataObservable == null){
dataObservable = apiService.getData()
.doOnError(throwable -> {
dataObservable = null;
})
.cache();
}
return dataObservable;
}
This works, but the code feels wrong. There's got to be a better way.
You can define the observable ahead of time, and it won't actually do anything until something subscribes to it. That's one less null value to worry about.
You can use the retry() operator, or a variant of it, to automatically retry the network operation on an error.
Finally, the cache() operator will ensure that only one network connection subscription is active. Each subscriber will get any updates from the observable, and will be oblivious to any network errors experienced.
Flowable<List<Data>> dataObservable = apiService.getData()
.retry()
.cache();
Apparently there is no operator for that.
The issue was discussed at Observable, retry on error and cache only if completed
and Plato created a nice tiny lib for that
platoblm/rx-onerror-retry-cache.
I am working on network module where it should provide offline data where I am fetching previously stored JSON response from local db and building response and returning to the application. All this is happening in my okhttp Interceptor at application level. I know I could have checked for n/w availability with ConnectivityManager
Every time I tried with n/w disabled or changing server false server URL for testing, Debugger just disappears call to chain.proceed(request)
Please note I knew I would have landed in onFaluire of retrofit in my app for any internet error. but I don't want that. Instead I want that to be handled inside interceptor and build response and return.
Searched on net found one question similar to this.
Let me know if any clarity needed on this.
I know this is very old post, but for those like me that also encounter this problem here's what I found.
The chain.proceed(request) call will throw an exception so any codes below this call won't be executed.
So if you really want to execute a codes when an exception occurred you may wrap the chain.proceed() call with try-catch.
override fun intercept(chain: Interceptor.Chain): Response {
try {
val response = chain.proceed(chain.request())
return response
} catch (ex : Exception) {
// handle the exception if you need to
}
// your codes here.
}
Warning: You'll lose access to the response when an exception occurred so you need to return an alternative response because it's required. And codes below try-catch statement won't be executed if there's no exception thrown by the chain.proceed() call.