I am using Retrofit for both asynchronous and synchronous api calls.
For both I have a custom error handler defined to handle unauthorised responses. For the synchronous calls I have declared the custom exception on the interface methods, I surround the interface implementation with a try/catch and it works perfect. I can catch Unauthorised Exceptions.
I have tried the same with asynchronous calls that use a callback and it doesn't work the same. Instead of the catching the Exception in the try/catch, I have to handle it in the failure method of the callback.
Here is the interface method:
#GET("getGardenGnomes")
void getGardenGnomes(#Header("Authorisation") String authorisation, Callback<GardenGnomes> callback) throws UnauthorisedException;
Here is the implementation:
void onClick() {
try {
getGardenGnomes()
} catch (UnauthorisedException exception) {
// .... handle the exception ....
}
}
void getGardenGnomes() throws UnauthorisedException {
// .... get client etc etc ....
client.getGardenGnomes(authorisation, new Callback<GardenGnomes>() {
#Override
public void success(GardenGnomes gardenGnomes, Response response) {
// .... do something ....
}
#Override
public void failure(RetrofitError error) {
// .... handle error ....
}
}
);
}
The question is:
Should I just handle the exception in the failure(RetrofitError error) method of the Callback and don't declare throws UnauthorisedException on the interface method of asynchronous calls?
Or what is the best way to implement this?
The anwser is yes. Using Retrofit interfaces you don't declare which exception is thrown from the implementation on the interface. RetrofitError is a RuntimeException therefore unchecked. It's expected that a RetrofitError will be thrown on failures from the Retrofit implementation and you're responsible for handling it accordingly. Using synchronous method you simply use the try/catch as you mentioned. Using the asynchronous method you handle it in the failure callback method.
public void methodToHandleRetrofitError(RetrofitError error) {
// handle the error
}
// Synchronous
try {
client.getGardenGnomes(authorization)
} catch (RetrofitError e) {
methodToHandleRetrofitError(e);
}
// Asynchronous
client.getGardenGnomes(authorisation, new Callback<GardenGnomes>() {
#Override
public void success(GardenGnomes gardenGnomes, Response response) {
// .... do something ....
}
#Override
public void failure(RetrofitError error) {
methodToHandleRetrofitError(error);
}
}
);
Hope this clarifies things for ya!
Related
I wish to handle all my responses in single method. the purpose is to recall the service when the response code is not 3, when the response code is 3 I intend to first refresh the token and then recall the same service.
I've created a BaseCallback class to catch one method but I can't see the log and can't catch breakpoints.
BASECALLBACK.class
public class BaseCallBack<T> implements Callback<T> {
#Override
public void onResponse(Call<T> call, Response<T> response) {
if (!response.isSuccessful()){
Log.d("BaseCallback","Response Fail");
}
}
#Override
public void onFailure(Call<T> call, Throwable t) {
t.toString();
}
}
CALL METHOD
ApiManager.getInstance().getUsers().enqueue(new BaseCallBack<List<User>>() {
#Override
public void onResponse(Call<List<User>> call, Response<List<User>> response) {
if (response.isSuccessful()){
}
}
#Override
public void onFailure(Call<List<User>> call, Throwable t) {
}
});
I just want to handle my services single method.
Your starting point is good - you have an ApiManager which is the single point you're looking for - a class, NOT a method (methods shouldn't be a single contact point in this case, it will make your code unreadable and harder to debug later.
From here it would probably be better to use your own custom interface, and implement it however you wish from where you call the request, there you can handle the stuff you want, this is a very generic example that should fix some stuff and get you going.
Be mindful to the fact that this still requires you to work - tweak and add the stuff you need.
This is all you need as an interface (very basic, you can add stuff)
public interface CustomCallListener<T>
{
public void getResult(T object);
}
This is how you should use it in you ApiManager - it receives your interface as a parameter carrying the expected object type, when the response returns do what you need - parse it, cut it, whatever - and cast it into the correct object, this example uses a String response and a List return object, you can expect whatever you think and parse it accordingly, Retrofit2 allows you to parse JSON strings directly (using GSON or some other library), so it's your decision on what to use here - if you don't know what I mean - read about it.
This is also where I would add breakpoints and Log. calls to debug the response you get as you get it you can also break down rawResponse for headers and other stuff.
class ApiManager
{
// .. other stuff you need...
public void getSomeList(final CustomCallListener<List<SomeObject>> listener)
{
Call<ResponseBody> request = serviceCaller.getSomeInfo(userid);
//...other stuff you might need...
request.enqueue(new Callback<ResponseBody>()
{
#Override
public void onResponse(Call<ResponseBody> call, retrofit2.Response<ResponseBody> rawResponse)
{
try
{
String response = rawResponse.body().string();
//...other stuff you might need...
//...do something with the response, convert it to
//return the correct object...
SomeObject object = new SomeObject(response);
listener.getResult(object);
}
catch (Exception e)
{
// .. the response was no good...
listener.getResult(null);
}
}
#Override
public void onFailure(Call<ResponseBody> call, Throwable throwable)
{
// .. the response was no good...
listener.getResult(null);
}
});
}
}
Finally this is what you should use from anywhere in your code - this allows you to implement the callback right there and handle anything you need by what you return from the ApiManager.
ApiManager.getInstance().getSomeList(new CustomCallListener<List<SomeObject>>()
{
#Override
public void getResult(List<SomeObject> objects)
{
if (null != objects)
{
// check objects and do whatever...
}
else
{
// ... do other stuff .. handle failure codes etc.
}
}
});
Stuff to notice
As mentioned - this is a very generic skeleton that can be greatly modified (add more methods to the interface with different return types to handle Exceptions, Bad responses, other objects, add more params to the same method to handle more options, etc.) read about the subject more, beware of passing null Objects, use try and catches to avoid crashes.
Hope this Helps!
My request should get either JSON for POJO or JSON described error(can be invalid request fields, server problems and so on).
But retrofit in subscriber gives me only Throwable. How can I find out is that a network error, what is http code, and get JSON with error?
private class ProjectListSubscriber extends Subscriber<ProjectListResponse> {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
//is that a network? http code? convert json to error POJO?
}
#Override
public void onNext(ProjectListResponse projectListResponse) {
updateProjectList(projectListResponse.getProjectList());
}
}
Since you are using RxJava, onError is called in case of network errors and endpoints related error are part of the Response.
In case of error, check if the throwable is an instance of HttpException
public void onError(Throwable e) {
if (e instanceof HttpException) {
if the check is true, the you have an error in your request. Cast the throwable to HttpException, and access is members. E.g.
((HttpException) e).response().errorBody()
if the check is false then you have a network related error.
I use Retrofit library to handle JSON response from my WCF web-service.
RestService.getRestService().search("tools", new Callback<SearchResult>() {
#Override
public void success(SearchResult searchResult, Response response) {
if( searchResult == null) return;
textView.setText(searchResult.toString());
} // end of success(
#Override
public void failure(RetrofitError error) {
showToast(R.string.internet_sikintisi);
}
});
I noticed that i get error if i leave the fragment or activity where i called this function from. Because I set text in textView where the activity or fragment is not already exists.
I modified the code to be like this:
RestService.getRestService().search("tools", new Callback<SearchResult>() {
#Override
public void success(SearchResult searchResult, Response response) {
if( searchResult == null) return;
try{
textView.setText(searchResult.toString());
} catch(Exception e) {}
} // end of success(
#Override
public void failure(RetrofitError error) {
try{
showToast(R.string.internet_sikintisi);
} catch(Exception e) {}
}
});
Basically, the problem solved, the app is not crashing every time the user gets in and out immediately. Still there might be a better solution for this problem.
I checked and Retrofit DOES NOT have cancel request feature!
You need to added checks success and failure methods the same way you would when using an asynctask check if the context still exist or make a isActive field that you set on onResume/onPause of the activity or fragment.
It is possible to cancel Retrofit requests in Retrofit 2(beta is available now.)
call.cancel();// as simple as that
I'm new in Robospice and I'm having a problem in handling error request. Is there a way that I can access the Response body from an error request in onRequestFailure method? If not, how you guys do it?
private class RequestListener implements RequestListener<Object> {
#Override
public void onRequestFailure(SpiceException e) {
}
#Override
public void onRequestSuccess(Object response) {
}
}
There's a suggestion that I should do the error checking inside the Spice Request. Any suggestion guys?
#Override
public SubscriptionsContainer loadDataFromNetwork() {
ResponseEntity<SubscriptionsContainer> response = null;
try {
response = getRestTemplate().exchange(/*your request data*/);
} catch (HttpClientErrorException e) {
String responsebody = e.getResponseBodyAsString();
}
}
If you throw any exception from loadDataFromNetwork(), it will be wrapped as SpiceException and passed to onRequestFailure() as its argument.
Therefore, you should check your ResponseEntity<T> for the conditions you expect and throw an exception in case those are not fulfilled.
If, on another hand, your RestTemplate.exchange call throws an exception, you could also handle it (as in your example) or let it be thrown (so it will be reported with onRequestFailure() on the main thread).
I am using Retrofit to access my API as follows:
public interface UserService {
...
#POST("/user/login")
public Observable<User> register(#Body() User user);
}
Here is how I access my API:
mUserService.register(user)
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<User>() {
#Override
public void onCompleted() {
....
}
#Override
public void onError(Throwable e) {
....
}
#Override
public void onNext(User user) {
....
}
});
This works perfectly well, except when there is an exception (i.e. IOException, or when connection times out), the onError method doesn't get fired, instead I get an exception on the main thread which terminates my application.
However, for some cases (such as when the API call responds with status code 400), the onError method is fired as expected.
After digging the logcat output, I have spotted this line, (not sure how I am supposed to deal with this)
rx.exceptions.OnErrorFailedException: Error occurred when trying to propagate error to Observer.onError
Can someone let me know where I am doing things wrong?
In my previous experience with RxJava the OnErrorFailedException means that an exception occurred while handling another exception in the onError method.
If you don't see stack trace of the real cause it's probably due the bug in RxJava with CompositeException (versions 0.19.2 and above) https://github.com/Netflix/RxJava/issues/1405
As a workaround try to wrap your onError code within try-catch block and log the exception. This way you will see what's the problem with your onError implementation and you will be able to solve the problem.
#Override
public void onError(Throwable e) {
try {
...
catch(Throwable e) {
// Log the exception
}
}
you can use unsafe unsafeSubscribe.
...
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.unsafeSubscribe(new Observer<User>() {
#Override
public void onCompleted() {
....
}
#Override
public void onError(Throwable e) {
....
}
#Override
public void onNext(User user) {
....
}
});
...