Handling error in RoboSpice - android

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).

Related

Can't figure out what to check for in OkHttp's onResponse method

I know there are lots of tutorials for OkHttp, but basically all of them do something different in the onResponse method and most don't bother to explain why. Some check for if (response.isSuccessful), some surround it with try/catch, some don't do any of this at all.
This is my example project. What is the proper way to handle the onResponse method?
public class MainActivity extends AppCompatActivity {
private TextView textViewResult;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textViewResult = findViewById(R.id.text_view_result);
OkHttpClient client = new OkHttpClient();
String url = "https://reqres.in/api/users?page=2";
Request request = new Request.Builder()
.url(url)
.build();
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
#Override
public void onResponse(Call call, Response response) throws IOException {
final String myResponse = response.body().string();
MainActivity.this.runOnUiThread(new Runnable() {
#Override
public void run() {
textViewResult.setText(myResponse);
}
});
}
});
}
}
Update
onResponse of okhttp runs on background thread. So, yes, it's necessary to do MainActivity.this.runOnUiThread(...).
Original answer
onResponse callback already runs on ui thread AFAIK. So, you don't actually need to do MainActivity.this.runOnUiThread(...).
And everyone's onResponse is different because everyone has different needs. Use try/catch if your operations in onResponse might give error and you don't want it to crash.
For some network requests you may need to check if response is successful for other you may not. It all depends on use cases. Do what works for you best.
I'd suggest you surround your code in onResponse in a try/catch block because the user might close the app before the network request is finished. And when you set the textview text in onResponse it will crash because the activity and that textview doesn't exist anymore.
Adding to the answer from rafid. There are basically three cases you want to check.
response.isSuccessful() => status code between 200 and 300
response.code() => to manually check after response is not successful
onFailure() => Network error or parsing error etc.
Ideally your callback would handle those cases something like
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
// network error or parsing error
}
#Override
public void onResponse(Call call, Response response) {
if (response.isSuccessful()) {
// do stuff all good
} else {
// handle different cases for different status codes or dump them all here
}
}
});
The reason you need a try-catch is because OkHttp is trying to parse the response. This is the case for example for response.errorBody().string();. Another case would be if your Callback<T> has actually a type parameter. Again OkHttp will try to parse the response to that type. If it fails it will result in a callback onto the onFailure method.
I think you need to make sure you know the legal response from the request, like an json or File. if it's just a json, use like below:
#Override
public void onResponse(Call call, Response response) throws IOException {
final String myResponse = response.body().string();
if (response.isSuccessful() && !TextUtils.isEmpty(myResponse)) {
MainActivity.this.runOnUiThread(new Runnable() {
#Override
public void run() {
textViewResult.setText(myResponse);
}
});
}
}
Edit: To be more clear.
Callback is running in mainThread so there is no need to call runOnUiThread.
If response is not successful you can try to parse error body as below. If response is successful you can parse with Gson as i show.
String message = "";
if (response.errorBody() != null) {
try {
message = response.errorBody().string();
} catch (IOException ignored) {
Log.e("OkHttp IOException", "error while parsing response");
}
Log.d("Error Message", message);
}
I recommend you to use Gson Library. First you should create your pojo class. You can use http://www.jsonschema2pojo.org/ to create your pojo class. Then you can parse body like below
Gson gson = new Gson();
MyPojo myPojo = gson.fromJson(response.body().charStream(), MyPojo.class);

How To Intercept a response that requires user action before replaying desired response to user

I have a rather unique problem I have to solve. I am working on an app that uses a homegrown networking layer with the legacy okhttp client in Android and we would like to use Retrofit and OKHttp3 instead.
The problem we have is that our services may return a response on a user request that contains one or more actions a user must take before the server will allow the original request to execute and thus return the desired response.
It goes kinda like this. User tries to log in, but they are a new user, instead services responds with a series of information collection actions. If the user cancels they do not log in, but if the user executes the series of actions than the original login request is replayed and the user logs in.
I am taking a close look at the Interceptor interface provided by OkHttp3 as the means of achieving this. Now I know I can intercept any response and modify it or do other things, but the interface method must return a value of type Response in the end. However I need the ability to "hold/prevent" that return from taking place before the user either A: Cancels the actions required by services, or B: Successfully completes those actions.
This is what I have so far.
private static class ServerActionInterceptor implements Interceptor {
#Override
public Response intercept(Chain chain) throws IOException {
Response response = chain.proceed(chain.request());
Gson gson = new Gson();
ServicesResponse serviceResponse = null;
if (response.body() != null) {
serviceResponse = gson.fromJson(response.body().string(), ServicesResponse.class);
}
if (serviceResponse != null) {
if (serviceResponse.getServerActions() != null && serviceResponse.getServerActions().size() > 0) {
//handle server actions
handleServerActions(serviceResponse);//this must complete before we can return chain.proceed IF there are actions the user must take.
}
}
return chain.proceed(chain.request());
}
//this method may end up returning a value
private void handleServerActions(ServicesResponse serviceResponse) {
//process server actions
}
}
Its not a whole lot, the majority will happen in handleServerActions and that honestly is pretty particular to the app. But the core of the issue, is what method should I employ so that the return will happen if there are no serverActions for the user request OR there are and the user has completed the actions and thus the true response is ready to be delivered to the client?
Thanks.
Due to a comment, I thought I would share my solution.
It involved a custom abstract class that implements Callback<T>.
This is just a general example and the actual implementation would be up to the business rules for your project.
public abstract class CustomCallback<T extends CustomResponse> implements Callback<T> {
private CustomExcetption encounteredException;
private Class<T> clazz;
public CustomCallback(Class<T> clazz) {
this.clazz = clazz;
}
#Override
public void onResponse(#NonNull Call<T> call, #NonNull Response<T> response) {
if (response.body() != null) {
T body = response.body();
i
if (body != null && body.hasCustomActions() != null && body.hasCustomActions().size() > 0) {
handleCustomAction(body);
} else {
onSuccessfulResponse(body);
}
} else if (response.errorBody() != null) {
onFailureResponse(exception);
}
}
#Override
public void onFailure(#NonNull Call<T> call, #NonNull Throwable throwable) {
onNetworkFailure(call, exception);
}
public abstract void onSuccessfulResponse(#Nullable T response);
public abstract void onFailureResponse(CustomException exception);
public abstract void onNetworkFailure(Call<T> call, CustomException exception);
public void handleCustomAction(#NonNull final T body, final String path) {
//handle custom actions.
if(customActionsHandledSuccessfully) {
onSuccessfulResponse(body);
} else {
//create your exception somehow
CustomException createdException = new CustomException();
onFailureResponse(createdException);
}
}
}
Ussage example:
Keep in ming the reference to SomeResponse.class is something I needed for my implementation and may not be needed in others. It was used for gson serialization purposes as I needed to know the concrete class of the response to serialize it properly.
service.someApi().enqueue(new CustomCallback<SomeResponse>(SomeResponse.class) {
#Override
public void onSuccessfulResponse(#Nullable SomeResponse response) {
}
#Override
public void onFailureResponse(CustomException exception) {
}
#Override
public void onNetworkFailure(Call<SomeResponse> call, CustomException exception) {
}
});

Handle errors in Retrofit 2 RX

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.

Canceling Retrofit requests or handling them when leaving the activity or fragment - android

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

Handling Unauthorised Errors in Retrofit for Asynchronous Calls

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!

Categories

Resources