RxJava , best way to network check before each api call - android

I'm using RxJava for my network calls and before each request I need to check the network.
Currently for each request I'm doing like this.
checkNetwork()
.andThen(netWorkCall())
.subscribe(new DisposableSubscriber<String>() {
#Override
public void onNext(String s) {
Log.d(TAG, "onNext: " + s);
}
#Override
public void onError(Throwable t) {
Log.d(TAG, "onError: " + t.getMessage());
}
#Override
public void onComplete() {
Log.d(TAG, "onComplete: ");
}
});
}
private Completable checkNetwork() {
return NetworkUtils.isConnected() ? Completable.complete() : Completable.error(new Throwable("Network Error"));
}
private Flowable<String> netWorkCall() {
return Flowable.just("Api response");
}
Is this the recommended way or is there any better way than this? Thanks in advance!!

Usually you don't check the network before. Better try to send the call and catch the errors when the request failed.
In your Repository which query the network you can prevalidate for a connection and throw an exception if the network connection failed.
That means:
networkCall()
.subscribe(
success -> { ... },
error -> { doSomethingWhenErrorHappened() }
);
private Flowable<String> netWorkCall() {
return networkAvailable() ? Flowable.just(...) : Flowable.error(...);
}
Thats the way it goes.
You should consider using a Single or Maybe since a network request usually returns only data onec or an error. Flowable is designed for using a stream.

Related

RxJava RetryWhen, This processor allows only a single Subscriber

I am learning how to do data polling in RxJava2
Here is my code so far.
private io.reactivex.Single<String> getMyTask() {
return io.reactivex.Single.fromCallable(new Callable<String>() {
#Override
public String call() throws Exception {
Log.d("ERSEN","Task Started!");
Random random = new Random(System.currentTimeMillis());
if(random.nextBoolean()){
return "WORK COMPLETED";
}
Log.d("ERSEN","Task Had An Error!");
throw new IllegalArgumentException();
}
});
}
The above is my Single which emits a String basically simulating some work.
I also make the task randomly succeed and fail to the test the case when a poll event fails to check if re-subscription occurs correctly
My problem
compositeDisposable.add(getMyTask()
.repeatWhen(new Function<Flowable<Object>, Publisher<?>>() {
#Override
public Publisher<?> apply(final Flowable<Object> objectFlowable) throws Exception {
return objectFlowable.delay(INTERVAL, TimeUnit.SECONDS);
}
})
.retryWhen(throwableFlowable -> throwableFlowable.flatMap(new Function<Throwable, Publisher<?>>() {
#Override
public Publisher<?> apply(Throwable throwable) throws Exception {
if (throwable instanceof ClassCastException) {
return Flowable.error(throwable);
}
return throwableFlowable.delay(INTERVAL, TimeUnit.SECONDS);
}
}))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(this::onSuccess, this::onError));
In the above, I am resubscribing to the Observable when it emitted some data successfully.
I am having problems with retryWhen.
For this example I wish to not retry if a ClassCastException occurs.
In my Observable this is not produced which is for a reason because I am testing the logic to retry only on certain errors
However, I am reviving this error with the above code when an error in the Observable is produced
This processor allows only a single Subscriber
I am not sure what is wrong, I have been following this blog post
http://blog.danlew.net/2016/01/25/rxjavas-repeatwhen-and-retrywhen-explained/
Thanks for reading
Let me know if you would like me to post any more details
You are resubscribing to the error flow in your retryWhen which is not allowed and doesn't make sense in your situation. You should delay a value in flatMap instead:
.retryWhen(throwableFlowable -> throwableFlowable.flatMap(
new Function<Throwable, Publisher<?>>() {
#Override
public Publisher<?> apply(Throwable throwable) throws Exception {
if (throwable instanceof ClassCastException) {
return Flowable.error(throwable);
}
return Flowable.just("ignored").delay(INTERVAL, TimeUnit.SECONDS);
}
}
))

RxJava .filter is blocking other code from sending data to API

I'm using RxJava inside an evernote job to send some data to API. This code was working just fine till now. The problem is somewhere in the .filter I think as it isn't even getting to getApiService().createReport(requestModel) method unless there are photos in the model (then the report.getImages() is not null.
public static Observable<Report> createReport(CreateReportModel model) {
return Observable.just(model)
.filter(report -> report.getImages() != null)
.flatMap(report -> {
return Observable.from(report.getImages())
.map(photoModel -> {
return photoModel;
})
.filter(photoModel -> photoModel.hasImage())
.filter(photoModel -> photoModel.getImage().exists())
.flatMap(photoModel -> uploadFile(photoModel)).toList();
})
.map(photoModels -> model)
.flatMap(requestModel -> {
return getApiService().createReport(requestModel)
.map(response -> {
return response;
});
});
}
This function is called inside this code
Observable<PalletReport> report = createReport(model);
report.subscribe(new Subscriber<PalletReport>() {
#Override
public void onCompleted() {
resultHolder.setResult(Result.SUCCESS);
#Override
public void onError(Throwable e) {
Timber.d(e, "Upload Error");
resultHolder.setResult(Result.RESCHEDULE);
}
#Override
public void onNext(PalletReport model) {
Timber.d("On Next " + model);
}
});
And here it goes to Result.SUCCESS but the response isn't get and the report isn't create on back end. My concern is that this code was working just fine a few days ago, and without any changes it stopped.
[EDIT]
I have this function that is called inside the first flatMap, and it's used to send the photos.
#NonNull
private static Observable<? extends CreatePalletPhotoModel> uploadPalletFile(CreatePalletPhotoModel photo) {
MultipartBody.Part fileBody = Paperless.createFileBody(photo.getImage());
return Paperless.getApiService().uploadPalletPhoto(fileBody)
.map(upload -> {
photo.setPalletStatus(upload.getPalletStatus());
photo.setImage(upload.getImage());
return photo;
});
}
If there are no reports after filter nothing will get executed. Consider removing
.map(photoModels -> model)
and just end the first observable there (you would need to subscribe to it) and start again with
Observable.just(model).flatMap(requestModel -> {
return getApiService().createReport(requestModel)
.map(response -> {
return response;
});
});
that will ensure that getApiService call is always executed.

RxJava: Continue next iteration even if error occurs

I'm using RxSearchView to emit out the results of a search query from an API to a recyclerview. However, if one of those query fails, onError() is called(which is expected) but the subscription as a whole is also canceled. Subsequent queries are not executed at all.
How should i modify the code so that the call to onError() is prevented when a query fails and the next incoming queries are executed normally?
Here's a code snippet:
subscription = RxSearchView.queryTextChanges(searchView)
.debounce(500, MILLISECONDS)
.filter(charSequence -> !TextUtils.isEmpty(charSequence))
.map(CharSequence::toString)
.subscribeOn(AndroidSchedulers.mainThread())
.observeOn(Schedulers.io())
.switchMap(query -> apiService.getSearchResults(query))
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<SearchResponse>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(SearchResponse searchResponse) {
if (searchResponse.getStatus().equals("OK")) {
//update Adapter
} else {
//update error views
}
}
});
P.S: I am using switchMap() so that the results of old queries are ignored, if the results of new query has arrived.
You have to handle this error and return an object instead. You can do it, for example, by using onErrorResumeNext operator with apiService.getSearchResults(query) call. What you are going to return - depends on you, you can even return null if you want, but better to create some wrapper which can carry both response status flag and normal response if received.
Something like:
subscription = RxSearchView.queryTextChanges(searchView)
.debounce(500, MILLISECONDS)
.filter(charSequence -> !TextUtils.isEmpty(charSequence))
.map(CharSequence::toString)
.subscribeOn(AndroidSchedulers.mainThread())
.observeOn(Schedulers.io())
.switchMap(query -> apiService
.getSearchResults(query)
.onErrorResumeNext(error -> null)
)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<SearchResponse>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(SearchResponse searchResponse) {
if (searchResponse != null && searchResponse.getStatus().equals("OK")) {
//update Adapter
} else {
//update error views
}
}
});
Of course, this is naive example with using null, in reality you need to write error handling logic. Better to return wrapper, because if using RxJava 2, then it doesn't support null.

Retrofit2 + RxJava error handling

I am using RxJava and Retrofit2 (with OkHttp as the HTTP client) to do networking and am trying to understand how different errors are handled by Retrofit2 and how they look from the RxJava side. The following code illustrates an RxJava Subscriber callback for a network call (made with Retrofit).
Subscription subscription = observable
.subscribeOn(mScheduler)
.observeOn(mAndroidScheduler)
.subscribe(new Subscriber<User>() {
#Override
public void onCompleted() {
Timber.d("onCompleted called");
mRetainerView.clearUserObservable();
mActivityView.hideProgressBar();
mActivityView.enableUi();
}
#Override
public void onError(Throwable e) {
Timber.d("onError called");
Timber.d(e.toString());
mRetainerView.clearUserObservable();
mActivityView.hideProgressBar();
mActivityView.enableUi();
}
#Override
public void onNext(User user) {
Timber.d("onNext called");
mRetainerView.clearUserObservable();
mActivityView.hideProgressBar();
mActivityView.enableUi();
mActivityView.launchMainActivity();
}
});
My question is, in what cases will onError() be called and once it's been called, how can I interrogate the Throwable to determine the cause?
From the Retrofit source it looks like the only Throwables that are possible to see are IOException and HttpException. Can anyone verify that that is true?
Here's the basics: onError() will be called if:
the observable you're subscribing to throws an exception (e.g. you get an IOException while trying to read a file)
an exception is raised in your onNext() method.
If there's an exception in your onComplete(), RxJava will propagate an rx.exceptions.OnCompletedFailedException and if there's an exception in onError() - you'll get rx.exceptions.OnErrorFailedException.
That said, you can just probe the Throwable you receive in your onError() method for exceptions that you're expecting. For example you know that if your API call results in client error (4xx), Retrofit will wrap it into HttpException. If there's a timeout with the request you'll get a SocketTimeoutException. Here's a rough example:
#Override
public void onError(Throwable e) {
Timber.d("onError called");
Timber.d(e.toString());
handleError(e);
}
private handleError(Throwable throwable) {
if (throwable instanceof HttpException) {
HttpException httpException = (HttpException)throwable;
int statusCode = httpException.code();
// handle different HTTP error codes here (4xx)
} else if (throwable instanceof SocketTimeoutException) {
// handle timeout from Retrofit
} else if (throwable instanceof IOException) {
// file was not found, do something
} else {
// generic error handling
mRetainerView.clearUserObservable();
mActivityView.hideProgressBar();
mActivityView.enableUi();
}
Do not use onError for flow. That'd be as bad as try-catch for flow.
Error HTTP codes, are valid responses and you should not deal with them in onError.
You can wrap the return type of your Retrofit services in Result, that gives you the means to get information about what happen with your call without throwing exceptions.
You can handle the state of your app using this pattern:
service.getSomething()
.map(r -> Model.success(r.response()))
.onErrorReturn(Model::error)
.observeOn(AndroidSchedulers.mainThread())
.startWith(Resource.loading())
.subscribe(r -> {
myProgressBar.setVisible(r.isLoading());
if (r.isSuccess()) {
handleSuccess(); // e.g. 400 is also success but needs handling
}
if (r.isError()) {
handleError();
}
}, OnErrorNotImplementedException::new);
See how I tried to handle all possible states within the stream and deliberately I throw OnErrorNotImplementedException for something I might've missed. This is very personal but I prefer to crash-fast-and-furious rather than being in an unknown state for a while that later will manifest in a crash harder to debug.
In Kotlin I have used bellow like..
disposable.add(apiService.getLogin_service(parment1,parment1)
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(object : DisposableSingleObserver<Login_Reg_Data_Model>() {
override fun onSuccess(model: Login_Reg_Data_Model) {
//success
}
override fun onError(e: Throwable) {
if (e is HttpException) {
// We had non-200 http error
Log.e("time exceptionr******>",e.message)
} else if (e is SocketTimeoutException) {
//time exception
Log.e("time exception******>",e.message)
} else if (e is IOException) {
// A network error
Log.e("network error******>",e.message)
} else {
//unknown error
Log.e("unknown error******>",e.message)
}
}
})
)

How to make multiple request and wait until data is come from all the requests in retrofit 2.0 - android

current code:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(Constant.BASEURL)
.addConverterFactory(GsonConverterFactory.create())
.build();
APIService service = retrofit.create(APIService.class);
Call<ResponseWrap> call = service.getNewsData();
call.enqueue(new Callback<ResponseWrap>() {
#Override
public void onResponse(Call<ResponseWrap> call1, Response<ResponseWrap> response) {
if (response.isSuccess()) {
ResponseWrap finalRes = response.body();
for(int i=0; i<finalRes.getResponse().getResults().size(); ++i){
String title = finalRes.getResponse().getResults().get(i).getWebTitle();
News n = new News(titleCategory, title, null);
newsList.add(n);
}
AdapterRecommendation adapter = new AdapterRecommendation(getApplicationContext(), newsList);
listView.setAdapter(adapter);
}
else{
Toast.makeText(getApplicationContext(), "onResponse - something wrong" + response.message(), Toast.LENGTH_LONG).show();
}
}
#Override
public void onFailure(Call<ResponseWrap> call1, Throwable t) {
Toast.makeText(getApplicationContext(), "exception: " + t.getMessage(), Toast.LENGTH_LONG).show();
}
});
works fine.
Now i want to make multiple calls (number of call will be decided at run time) and all calls gives data in same format. data from all calls needs to be add to newsList. Once data is available from all calls and added to newsList, call
AdapterRecommendation adapter = new AdapterRecommendation(getApplicationContext(), newsList);
listView.setAdapter(adapter);
Can anyone help me what is the best way to get data from multiple calls and wait until all request is not over in retrofit 2.0.
The clean and neat approach to wait until all your requests will be done is to use Retrofit2 in conjunction with RxJava2 and its zip function.
What zip does is basically constructs new observable that waits until all your retrofit Observable requests will be done and then it will emit its own result.
Here is an example Retrofit2 interface with Observables:
public interface MyBackendAPI {
#GET("users/{user}")
Observable<User> getUser(#Path("user") String user);
#GET("users/{user}/photos")
Observable<List<Photo>> listPhotos(#Path("user") String user);
#GET("users/{user}/friends")
Observable<List<User>> listFriends(#Path("user") String user);
}
In the code where you going to make multiple requests and only after all of them will complete do something else you can then write the following:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.example.com/")
.build();
MyBackendAPI backendApi = retrofit.create(MyBackendAPI.class);
List<Observable<?>> requests = new ArrayList<>();
// Make a collection of all requests you need to call at once, there can be any number of requests, not only 3. You can have 2 or 5, or 100.
requests.add(backendApi.getUser("someUserId"));
requests.add(backendApi.listPhotos("someUserId"));
requests.add(backendApi.listFriends("someUserId"));
// Zip all requests with the Function, which will receive the results.
Observable.zip(
requests,
new Function<Object[], Object>() {
#Override
public Object apply(Object[] objects) throws Exception {
// Objects[] is an array of combined results of completed requests
// do something with those results and emit new event
return new Object();
}
})
// After all requests had been performed the next observer will receive the Object, returned from Function
.subscribe(
// Will be triggered if all requests will end successfully (4xx and 5xx also are successful requests too)
new Consumer<Object>() {
#Override
public void accept(Object o) throws Exception {
//Do something on successful completion of all requests
}
},
// Will be triggered if any error during requests will happen
new Consumer<Throwable>() {
#Override
public void accept(Throwable e) throws Exception {
//Do something on error completion of requests
}
}
);
That's all :)
Just in case wanna show how the same code looks like in Kotlin.
val retrofit = Retrofit.Builder()
.baseUrl("https://api.example.com/")
.build()
val backendApi = retrofit.create(MyBackendAPI::class.java)
val requests = ArrayList<Observable<*>>()
requests.add(backendApi.getUser())
requests.add(backendApi.listPhotos())
requests.add(backendApi.listFriends())
Observable
.zip(requests) {
// do something with those results and emit new event
Any() // <-- Here we emit just new empty Object(), but you can emit anything
}
// Will be triggered if all requests will end successfully (4xx and 5xx also are successful requests too)
.subscribe({
//Do something on successful completion of all requests
}) {
//Do something on error completion of requests
}
If you don't mind adding one more dependency you could use RxAndroid.
In particular, you should change your Service interface with something similar to this:
#GET("/data")
Observable<ResponseWrap> getNewsData();
Now, you can do this:
Observable
.range(0, **numberOfTimes**, Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.doOnError(new Action1<Throwable>() {
#Override
public void call(Throwable throwable) {
Log.e("error", throwable.toString());
}
})
.concatMap(new Func1<Integer, Observable<ResponsWrapper>>() {
#Override
public Observable<ResponsWrapper> call(Integer integer) {
Log.i("news", "nr:" + integer);
//Does the call.
return service.getNewsData(integer);
}
}).concatMap(new Func1<ResponsWrapper, Observable<News>>() {
#Override
public Observable<News> call(final ResponsWrapper responsWrapper) {
return Observable.fromCallable(new Func0<News>() {
#Override
public News call() {
//change the result of the call to a news.
return new News(responsWrapper.category,responsWrapper.title,null);
}
});
}
}).toList().subscribe(new Action1<List<News>>() {
#Override
public void call(List<News> newList) {
AdapterRecommendation adapter = new AdapterRecommendation(getApplicationContext(), newsList);
listView.setAdapter(adapter);
}
});
Just change numberOfTimes and it will work! Hope it helps.
P.s. maybe there are cleaner ways to do this.
You can achieve it by making synchronous retrofit calls. To avoid NetworkOnUiException, I am doing this inside asynctask.
List<Something> list = new ArrayList();
public void doInBackground(){
for(int i = 0; i < numberOfCalls; i++){
Call<Something> call = service.method1("some_value");
List<Something> list = call1.execute().body();
list.add(list1);
}
}
public void onPostExecute(){
AdapterRecommendation adapter = new AdapterRecommendation(getApplicationContext(), newsList);
listView.setAdapter(adapter);
}
This will ensure that the second call happens only after the first one has completed.
If you are using rx-java, you can use Zip/flatMap operator as used in this answer.
for anybody checking this question. This works for me (Kotlin)
fun manyRequestsNetworkCall(requests: ArrayList<Observable<*>>, activity: Activity){
Observable.zip(requests){results ->
activity.runOnUiThread(Runnable {
//do something with those results
// runOnUiThread solves the problem cannot do something on background thread
})
// observeOn and subscribeOn solvesthe problem of NetworkOnMainThreadException
}.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.doOnSubscribe { userWorkdysResponse.value = Response.loading((requestType)) }
.subscribe ({
// do something when all the requests are done
},{
// do something if there is an error
})
}
Here is a solution based on kotlin coroutines.
//turn the request methods into suspend functions
#GET("data1")
suspend fun getData(): Response<Data1>
#GET("data2")
suspend fun getData2(): Response<Data2>
//define a data class to ecapsulate data from several results
class Data{
val data1: Data1,
val data2: Data2
}
//generic class to encapsulate any request result
sealed class Result<out T : Any?> {
data class Success<out T : Any?>(val data: T) : Result<T>()
data class Error(val message: String, val exception: Exception?) : Result<Nothing>()
}
scope.launch {
val result = withContext(Dispatchers.IO) {
try {
//start two requests in parallel
val getData1Task = async { webservice.getData1() }
val getData2Task = async { webservice.getData2() }
//await for both to finish
val data1Response = getData1Task.await()
val data2Response = getData2Task.await()
//process the response
if (data1Response.isSuccessful && data2Response.isSuccessful)
Result.Success(Data(data1Response.body()!!,data2Response.body()!!))
else
Result.Error("server error message", null)
} catch (e: Exception) {
Result.Error(e.message.orEmpty(), e)
}
}
//main thread
result.run {
when (this) {
is Result.Success -> {
//update UI
}
is Result.Error -> {
toast(message)
log(message)
}
}
}
}

Categories

Resources