I am using Volley for my Android app to fetch data from my server. It works well except when handling the error from my server. My server sends this response when there is a mistake:
{
"status": 400,
"message": "Errors (2): A name is required- Julien is already used. Not creating."
}
My goal is to get the message and then display it in a Toast. I followed some sample for how to do this, but it doesn't work.
There is my error listener :
public void onErrorResponse(VolleyError error) {
int statusCode = error.networkResponse.statusCode;
NetworkResponse response = error.networkResponse;
Log.d("testerror",""+statusCode+" "+response.data);
// Handle your error types accordingly.For Timeout & No connection error, you can show 'retry' button.
// For AuthFailure, you can re login with user credentials.
// For ClientError, 400 & 401, Errors happening on client side when sending api request.
// In this case you can check how client is forming the api and debug accordingly.
// For ServerError 5xx, you can do retry or handle accordingly.
if( error instanceof NetworkError) {
} else if( error instanceof ClientError) {
} else if( error instanceof ServerError) {
} else if( error instanceof AuthFailureError) {
} else if( error instanceof ParseError) {
} else if( error instanceof NoConnectionError) {
} else if( error instanceof TimeoutError) {
}
showProgress(false);
mPasswordView.setError(getString(R.string.error_incorrect_password));
mPasswordView.requestFocus();
}
And there the result of my debugger : testerror﹕ 400 [B#430b8d60
EDIT: Moreover my error.getMessage() is null.
So I don't understand why my variable response.data is not the response from my server.
If someone know how I can get the message from my server it's will be cool.
Thx,
I've implemented something similar to this, and it's relatively simple. Your log message is printing out what looks like gibberish, because response.data is really a byte array - not a String. Also, a VolleyError is really just an extended Exception, so Exception.getMessage() likely wouldn't return what you are looking for unless you override the parsing methods for parsing your VolleyError in your extended Request class. A really basic way to handle this would be to do something like:
//In your extended request class
#Override
protected VolleyError parseNetworkError(VolleyError volleyError){
if(volleyError.networkResponse != null && volleyError.networkResponse.data != null){
VolleyError error = new VolleyError(new String(volleyError.networkResponse.data));
volleyError = error;
}
return volleyError;
}
}
If you add this to your extended Request classes, your getMessage() should at least not return null. I normally don't really bother with this, though, since it's easy enough to do it all from within your onErrorResponse(VolleyError e) method.
You should use a JSON library to simplify things -- I use Gson for example or you could use Apache's JSONObjects which shouldn't require an additional external library. The first step is to get the response JSON sent from your server as a String (in a similar fashion to what I just demonstrated), next you can optionally convert it to a JSONObject (using either apache's JSONObjects and JsonArrays, or another library of your choice) or just parse the String yourself. After that, you just have to display the Toast.
Here's some example code to get you started:
public void onErrorResponse(VolleyError error) {
String json = null;
NetworkResponse response = error.networkResponse;
if(response != null && response.data != null){
switch(response.statusCode){
case 400:
json = new String(response.data);
json = trimMessage(json, "message");
if(json != null) displayMessage(json);
break;
}
//Additional cases
}
}
public String trimMessage(String json, String key){
String trimmedString = null;
try{
JSONObject obj = new JSONObject(json);
trimmedString = obj.getString(key);
} catch(JSONException e){
e.printStackTrace();
return null;
}
return trimmedString;
}
//Somewhere that has access to a context
public void displayMessage(String toastString){
Toast.makeText(context, toastString, Toast.LENGTH_LONG).show();
}
try this class to handle all erros
public class VolleyErrorHelper {
/**
* Returns appropriate message which is to be displayed to the user
* against the specified error object.
*
* #param error
* #param context
* #return
*/
public static String getMessage (Object error , Context context){
if(error instanceof TimeoutError){
return context.getResources().getString(R.string.timeout);
}else if (isServerProblem(error)){
return handleServerError(error ,context);
}else if(isNetworkProblem(error)){
return context.getResources().getString(R.string.nointernet);
}
return context.getResources().getString(R.string.generic_error);
}
private static String handleServerError(Object error, Context context) {
VolleyError er = (VolleyError)error;
NetworkResponse response = er.networkResponse;
if(response != null){
switch (response.statusCode){
case 404:
case 422:
case 401:
try {
// server might return error like this { "error": "Some error occured" }
// Use "Gson" to parse the result
HashMap<String, String> result = new Gson().fromJson(new String(response.data),
new TypeToken<Map<String, String>>() {
}.getType());
if (result != null && result.containsKey("error")) {
return result.get("error");
}
} catch (Exception e) {
e.printStackTrace();
}
// invalid request
return ((VolleyError) error).getMessage();
default:
return context.getResources().getString(R.string.timeout);
}
}
return context.getResources().getString(R.string.generic_error);
}
private static boolean isServerProblem(Object error) {
return (error instanceof ServerError || error instanceof AuthFailureError);
}
private static boolean isNetworkProblem (Object error){
return (error instanceof NetworkError || error instanceof NoConnectionError);
}
Related
I am using TMDb API and it returns two different HttpException errorBody type.
One is a proper ApiStatus model which contains status code and message in json.
Other is an error array in json.
So I need to check these two types and convert them to proper Error models.(ApiStatus and Error pojos)
Here is what I'm trying to do to handle two cases:
public abstract class DisposableSingleObserverWrapper<T> extends DisposableSingleObserver<T> {
public abstract void onSuccess(T t);
public abstract void onFail(Throwable e);
#Override
public void onError(#NonNull Throwable e) {
if (e instanceof HttpException) {
ResponseBody body = ((HttpException) e).response().errorBody();
try {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http:/fakeapi/")
.addConverterFactory(GsonConverterFactory.create())
.build();
//New status code handling in tmdb api
Converter<ResponseBody, ApiStatus> errorConverter2 = retrofit.responseBodyConverter(ApiStatus.class, new Annotation[0]);
ApiStatus apiStatus = errorConverter2.convert(body);
if (apiStatus != null && !TextUtils.isEmpty(apiStatus.getStatusMessage())) {
onFail(new Throwable(apiStatus.getStatusMessage()));
}
else {
//Old case of handling status codes in tmdb api
Converter<ResponseBody, Error> errorConverter = retrofit.responseBodyConverter(Error.class, new Annotation[0]);
Error error = errorConverter.convert(body);
if (error != null & error.getErrorList()!=null && error.getErrorList().size()>0) {
onFail(new Throwable(error.getErrorList().get(0)));
}
else{
onFail(new Throwable(((HttpException) e).response().code() + " - " + ((HttpException) e).response().message()));
}
}
} catch (IOException ioException) {
onFail(new Throwable(((HttpException) e).response().code() + " - " + ((HttpException) e).response().message()));
}
} else {
onFail(e);
}
}
public class Error {
#SerializedName("errors")
public ArrayList<String> errorList;
public ArrayList<String> getErrorList() {
return errorList;
}
}
}
This piece of work doesn't work as intended because ResponseBody is consumed in first trial of converting and in second case it has no data. My question is how can I clone ResponseBody or what kind of approach should I follow to deal with this situation?
How to handle error response with Retrofit 2 using synchronous request?
I need process response that in normal case return pets array and if request has bad parametrs return error json object. How can I process this two situations?
I am trying to use this tutorial but the main problem is mapping normal and error json to objects.
My normal response example:
[ {
"type" : "cat",
"color": "black"
},
{
"type" : "cat",
"color": "white"
} ]
Error response example:
{"error" = "-1", error_description = "Low version"}
What I got:
Call<List<Pet>> call = getApiService().getPet(1);
Response<List<Pet>> response;
List<Pet> result = null;
try {
response = call.execute(); //line with exception "Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 2 path"
if(!response.isSuccessful()){
Error error = parseError(response);
Log.d("error message", error.getErrorDescription());
}
if (response.code() == 200) {
result = response.body();
}
} catch (IOException e) {
e.printStackTrace();
}
Retrofit 2 has a different concept of handling "successful" requests than Retrofit 1. In Retrofit 2, all requests that can be executed (sent to the API) and for which you’re receiving a response are seen as "successful". That means, for these requests, the onResponse callback is fired and you need to manually check whether the request is actually successful (status 200-299) or erroneous (status 400-599).
If the request finished successfully, we can use the response object and do whatever we wanted. In case the error actually failed (remember, status 400-599), we want to show the user appropriate information about the issue.
For more details refer this link
After going through a number of solutions. Am posting it for more dynamic use. Hope this will help you guys.
My Error Response
{
"severity": 0,
"errorMessage": "Incorrect Credentials (Login ID or Passowrd)"
}
Below is as usual call method
private void makeLoginCall() {
loginCall = RetrofitSingleton.getAPI().sendLogin(loginjsonObject);
loginCall.enqueue(new Callback<Login>() {
#Override
public void onResponse(Call<Login> call, Response<Login> response) {
if (response != null && response.code() == 200){
//Success handling
}
else if (!response.isSuccessful()){
mServerResponseCode = response.code();
Util.Logd("In catch of login else " + response.message());
/*
* Below line send respnse to Util class which return a specific error string
* this error string is then sent back to main activity(Class responsible for fundtionality)
* */
mServerMessage = Util.parseError(response) ;
mLoginWebMutableData.postValue(null);
loginCall = null;
}
}
#Override
public void onFailure(Call<Login> call, Throwable t) {
Util.Logd("In catch of login " + t.getMessage());
mLoginWebMutableData.postValue(null);
mServerMessage = t.getMessage();
loginCall = null;
}
});
}
Below Is util class to handle parsing
public static String parseError(Response<?> response){
String errorMsg = null;
try {
JSONObject jObjError = new JSONObject(response.errorBody().string());
errorMsg = jObjError.getString("errorMessage");
Util.Logd(jObjError.getString("errorMessage"));
return errorMsg ;
} catch (Exception e) {
Util.Logd(e.getMessage());
}
return errorMsg;
}
Below in viewModel observer
private void observeLogin() {
loginViewModel.getmLoginVModelMutableData().observe(this, login -> {
if (loginViewModel.getSerResponseCode() != null) {
if (loginViewModel.getSerResponseCode().equals(Constants.OK)) {
if (login != null) {
//Your logic here
}
}
//getting parsed response message from client to handling class
else {
Util.stopProgress(this);
Snackbar snackbar = Snackbar.make(view, loginViewModel.getmServerVModelMessage(), BaseTransientBottomBar.LENGTH_INDEFINITE).setAction(android.R.string.ok, v -> { });
snackbar.show();
}
} else {
Util.stopProgress(this);
Snackbar snackbar = Snackbar.make(view, "Some Unknown Error occured", BaseTransientBottomBar.LENGTH_INDEFINITE).setAction(android.R.string.ok, v -> { });
snackbar.show();
}
});
}
I think you should create a generic response class (let's say GenericResponse), that is extended by a specific response class (let's say PetResponse). In the first one, you include generic attributes (error, error_description), and in the latter, you put your specific response data (List<Pet>).
In your case, I would go with something like this:
class GenericResponse {
int error;
String error_description;
}
class PetResponse extends GenericResponse {
List<Pet> data;
}
So, your successful response body should look like this:
{
"data": [ {
"type" : "cat",
"color": "black"
},
{
"type" : "cat",
"color": "white"
} ]
}
And your error response body should look like this:
{ "error" = "-1", error_description = "Low version"}
Finally, your response object that is returned from the API call should be:
Response<PetResponse> response;
Wrap all your calls in retrofit2.Response like so:
#POST("user/login")
suspend fun login(#Body form: Login): Response<LoginResponse>
A very simple retrofit 2 error handler:
fun <T> retrofitErrorHandler(res: Response<T>): T {
if (res.isSuccessful) {
return res.body()!!
} else {
val errMsg = res.errorBody()?.string()?.let {
JSONObject(it).getString("error") // or whatever your message is
} ?: run {
res.code().toString()
}
throw Exception(errMsg)
}
}
Then use them like:
try {
createdReport = retrofitErrorHandler(api...)
} catch (e: Exception) {
toastException(ctx = ctx, error = e)
}
I have a repository class that must return this: Observable<List<SomeObject>,
I do this:
#Override
public Observable<List<SomeObject>> getAllById(Long id) {
if (!AndroidUtils.isNetworkAvailable(mContext))
return Observable.error(new NoNetworkConnectionException());
return mRestService.get(id);
}
This approach works normally, the problem is I want to return custom exceptions
in case of failures, but I don't know the best way to do this with rxjava.
So far, the only solution that works is something like that:
#Override
public Observable<List<SomeObject>> getAllById(Long id) {
if (!AndroidUtils.isNetworkAvailable(mContext))
return Observable.error(new NoNetworkConnectionException());
return Observable.create(subscriber -> {
mRestService.get(id).subscribe(new Observer<List<SomeObject>>() {
#Override
public void onCompleted() {
subscriber.onCompleted();
}
#Override
public void onError(Throwable e) {
if (e instanceof HttpException && ((HttpException) e).code() == 401)
subscriber.onError(new UnathorizedException());
else
subscriber.onError(e);
}
#Override
public void onNext(List<SomeObject> objects) {
subscriber.onNext(objects);
}
});
});
}
I know that is not a good thing to use Observable.create, but I can't figure out
another way to do this.
RestService is this:
public interface RestService {
#GET("objects/{id}")
Observable<List<SomeObject>> get(#Path("id") Long id);
}
If anyone knows a better approach, please tell me.
Thanks!
You can use the operator onErrorResumeNext to map your exception to another one.
mRestService.get(id)
.onErrorResumeNext(e -> {
if (e instanceof HttpException && ((HttpException) e).code() == 401)
return Observable.error(new UnathorizedException());
else
return Observable.error(e);
})
.subscribe();
I did this in a project by adding an interceptor when creating the rest service. This way the errors are checked before the request reaches your rest service.
OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder();
httpClientBuilder.addInterceptor(new ErrorInterceptor());
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(myBaseUrl)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.client(httpClientBuilder.build())
.build();
The ErrorInterceptor class looks like
public class ErrorInterceptor implements Interceptor {
private static final Charset UTF8 = Charset.forName("UTF-8");
#Override
public Response intercept(Chain chain) throws IOException {
Request originalRequest = chain.request();
Response response = chain.proceed(originalRequest);
if (response.code() >= 400) {
throwError(response);
return response;
} else {
return response;
}
}
private void throwError (Response response) throws IOException {
ResponseBody responseBody = response.body();
BufferedSource source = responseBody.source();
source.request(Long.MAX_VALUE); // Buffer the entire body.
Buffer buffer = source.buffer();
Charset charset = UTF8;
MediaType contentType = responseBody.contentType();
if (contentType != null) {
charset = contentType.charset(UTF8);
}
if (responseBody.contentLength() != 0) {
String responseJSON = buffer.clone().readString(charset);
Gson gson = new Gson();
Type type = new TypeToken<ErrorResponse>() {}.getType();
ErrorResponse error = null;
try {
error = gson.fromJson(responseJSON, type);
}
catch (Exception e) {
int a = 1;
}
if (error != null && error.hasErrors())
throw ErrorMapper.mapError(error.getFirstError());
}
}
}
And my ErrorResponse class
public class ErrorResponse {
private List<Error> errors;
public boolean hasErrors () {
return errors != null && errors.size() > 0;
}
public Error getFirstError() {
if (errors == null || errors.size() == 0) return null;
return errors.get(0);
}
}
In my ErrorMapper I just check the error message against a set of possible messages from the server and create a new Error containing the message to display on the client.
I'm just checking the first error here, but you should easily be able to adopt it to multiple errors.
You can try following:
RestService restService = Mockito.mock(RestService.class);
Observable<List<Object>> networkExOrEmpty = isNetworkAvailable() ?
Observable.empty() :
Observable.error(new NoNetworkConnectionException());
Observable<List<Object>> resultOrEx = restService
.getAllById(42L)
.materialize()
.map(res -> {
if (res.isOnError()) {
Throwable err = res.getThrowable();
if (err instanceof HttpException && ((HttpException) err).code() == 401) {
return Notification.createOnError(new UnauthrizedException());
} else {
return Notification.createOnError(err);
}
} else {
return res;
}
}).dematerialize();
Observable<List<Object>> result = networkExOrEmpty.concatWith(resultOrEx);
Start with Observable which emits either Error or nothing, depending on network conectivity state, then concatenate it with result from Retrofit service. Observable.materialize() allows one to act on error items: push appropriate exception downstream, and pass non-error notifications as is.
Volley works well when there is network but it goes weird when network drops.
When network is doping ie network strength is going down and i make a post request then nothing happens but once the network regain to full strength it makes the call to the previous post request not the new one?
when i clear the cache it works well...
how to solve this issue, why i am getting previous post response?
// Implements required listeners
public class VolleyServices<T> implements Response.Listener<T>, ErrorListener{
private int id; // request id
private RequestQueue request; // volley request queue
private VolleyResponce<T> listener; // callback listener
private Class<T> clazz; // parsing class
public interface VolleyResponce<T> {
public void OnSuccess(T response,int id);
public void OnError(ServerError error,int id);
}
// Constructor
public VolleyServices(VolleyResponce<T> listener,Context context) {
this.listener = listener;
request = VolleyRequest.getInstance(context).getRequestQueue();
}
// call to post the request with parse class, post parameters in map and request id
public void post(String methodName, HashMap<String, String> map,Class<T> clazz,int id) {
String url = baseUrl + methodName;
this.clazz = clazz;
this.id = id;
GsonRequest<T> myReq = new GsonRequest<T>(Request.Method.POST,url,clazz,map,this,this);
myReq.setTag(id);
request.add(myReq);
}
#Override
public void onResponse(T response) {
if(response != null && clazz == response.getClass()){
listener.OnSuccess(response,this.id);
}
}
#Override
public void onErrorResponse(VolleyError volleyError) {
ServerError error = null;
if(volleyError instanceof NetworkError) {
error = new ServerError("No internet Access, Check your internet connection.","400");
}
if(volleyError instanceof AuthFailureError) {
error = new ServerError("Authentication Failure","400");
}
if(volleyError instanceof ParseError) {
error = new ServerError("Parsing error","400");
}
if(volleyError instanceof NoConnectionError) {
error = new ServerError("No internet Access, Check your internet connection.","400");
}
if(volleyError instanceof TimeoutError) {
error = new ServerError("Request timed out, Please try again later.","400");
}
if(volleyError.networkResponse != null && volleyError.networkResponse.statusCode == 500) {
error = new ServerError("Internal server error","400");
}
else {
try {
if(volleyError.networkResponse != null) {
String responseBody = new String(volleyError.networkResponse.data, "utf-8" );
try{
Gson gson = new Gson();
error = gson.fromJson(responseBody, ServerError.class);
if(error.status == null) {
error.status = "";
}
}catch(Exception e){
e.printStackTrace();
}
} else {
error = new ServerError("Unknown Error","400");
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
listener.OnError(error,this.id);
}
Some log message i captured which explains everything in detail
log link
in my application, I've been thinking of the best way to implement 5xx responses from the server.
The first approach was to write my own version of the Request.deliverError method as attached:
#Override
public void deliverError(VolleyError error) {
if(error.networkResponse == null){
super.deliverError(error);
return;
}else {
switch(error.networkResponse.statusCode){
case HttpStatus.SC_HTTP_VERSION_NOT_SUPPORTED:
AppInfo.reportDevInfo(GlideApplication.applicationContext, "got a 505 response for request" +this.toString(), null);
break;
case HttpStatus.SC_INTERNAL_SERVER_ERROR:
case HttpStatus.SC_BAD_GATEWAY:
case HttpStatus.SC_SERVICE_UNAVAILABLE:
case HttpStatus.SC_GATEWAY_TIMEOUT:
int retryCount = RETRY_COUNT - getRetryPolicy().getCurrentRetryCount();
if(retryCount < 0) {
super.deliverError(error);
return;
}
String backoff = error.networkResponse.getHeaders.get("Retry-After");
if(TextUtils.isEmpty(backoof) == false) {
attemptRetryWithNewBackoff(backoff);
return;
}
break;
}
super.deliverError(error)
}
}
}
but that only caused ANRs in the application.
Looking at further research, I found this blog post that showed a way of handling the different response codes, the only issue, is that I'm not sure how to generalise this to my entire application and implementing handling of 5xx response codes with the appropriate "Retry-After" heade.
Having a class that implements ErrorListener and gets a different one as a param in the constructor seems very costly and inefficient:
public class MyErrorListener implements ErrorListener {
ErrorListener mListener;
public MyErrorListener(ErrorListener listener) {
this.mListener = listener;
}
#Override
public void onErrorResponse(VolleyError error) {
if(handleFiveHundredResponse(error) == false) {
this.mListener.onErrorResponse(error);
}
}
}
I know the answer is a bit late, but I'm sure it will definitely help newbies looking for this and This is what I do.
In Kotlin :
Request
val request = object : JsonObjectRequest(Method.POST,
url,
reqObj,
{}, { error ->
Toast.makeText(this, getVolleyError(error), Toast.LENGTH_LONG).show()
}) {
}
RequestController.getInstance().addToRequestQueue(request)
getVolleyError(error: VolleyError)
fun Activity.getVolleyError(error: VolleyError): String {
var errorMsg = ""
if (error is NoConnectionError) {
val cm = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
var activeNetwork: NetworkInfo? = null
activeNetwork = cm.activeNetworkInfo
errorMsg = if (activeNetwork != null && activeNetwork.isConnectedOrConnecting) {
"Server is not connected to the internet. Please try again"
} else {
"Your device is not connected to internet.please try again with active internet connection"
}
} else if (error is NetworkError || error.cause is ConnectException) {
errorMsg = "Your device is not connected to internet.please try again with active internet connection"
} else if (error.cause is MalformedURLException) {
errorMsg = "That was a bad request please try again…"
} else if (error is ParseError || error.cause is IllegalStateException || error.cause is JSONException || error.cause is XmlPullParserException) {
errorMsg = "There was an error parsing data…"
} else if (error.cause is OutOfMemoryError) {
errorMsg = "Device out of memory"
} else if (error is AuthFailureError) {
errorMsg = "Failed to authenticate user at the server, please contact support"
} else if (error is ServerError || error.cause is ServerError) {
errorMsg = "Internal server error occurred please try again...."
} else if (error is TimeoutError || error.cause is SocketTimeoutException || error.cause is ConnectTimeoutException || error.cause is SocketException || (error.cause!!.message != null && error.cause!!.message!!.contains(
"Your connection has timed out, please try again"
))
) {
errorMsg = "Your connection has timed out, please try again"
} else {
errorMsg = "An unknown error occurred during the operation, please try again"
}
return errorMsg
}
The method I used above is an extension function of kotlin, which can only be called from an activity, if you want to call the same method from another scope, then you have to modify it. an instance of an activity is mandatory to check the internet connectivity
In Java:
Request
JsonObjectRequest request=new JsonObjectRequest(Request.Method.POST,"url",null,new Response.Listener<JSONObject>(){
#Override
public void onResponse(JSONObject response){
}
},new Response.ErrorListener(){
#Override
public void onErrorResponse(VolleyError error){
Toast.makeText(this, getVolleyError(error), Toast.LENGTH_LONG).show()
}
});
RequestController.getInstance().addToRequestQueue(request);
getVolleyError(VolleyError error)
public String getVolleyError(VolleyError error){
String errorMsg="";
if(error instanceof NoConnectionError){
ConnectivityManager cm=(ConnectivityManager)activity.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetwork=null;
if(cm!=null){
activeNetwork=cm.getActiveNetworkInfo();
}
if(activeNetwork!=null&&activeNetwork.isConnectedOrConnecting()){
errorMsg="Server is not connected to internet.kindly try agian";
}else{
errorMsg="Your device is not connected to internet.please try again with active internet connection";
}
}else if(error instanceof NetworkError||error.getCause()instanceof ConnectException){
errorMsg="Your device is not connected to the internet.";
}else if(error.getCause()instanceof MalformedURLException){
errorMsg="That was a bad request please try again...";
}else if(error instanceof ParseError||error.getCause()instanceof IllegalStateException
||error.getCause()instanceof JSONException
||error.getCause()instanceof XmlPullParserException){
errorMsg="There was an error parsing data...";
}else if(error.getCause()instanceof OutOfMemoryError){
errorMsg="Device out of memory";
}else if(error instanceof AuthFailureError){
errorMsg="Failed to authenticate user at the server, please contact support";
}else if(error instanceof ServerError||error.getCause()instanceof ServerError){
errorMsg="Internal server error occurred please try again....";
}else if(error instanceof TimeoutError||error.getCause()instanceof SocketTimeoutException
||error.getCause()instanceof ConnectTimeoutException
||error.getCause()instanceof SocketException
||(error.getCause().getMessage()!=null
&&error.getCause().getMessage().contains("Connection timed out"))){
errorMsg="Your connection has timed out, please try again";
}else{
errorMsg="Sorry, some thing weird occurred";
}
return errorMsg;
}
The method above should need an instance of an activity to check connectivity, you can provide it as per your logic
I have implemented my own ErrorListener which handle server error responses, here is the code:
public class MyErrorListener implements Response.ErrorListener {
Context context;
View errorView;
TextView errorText;
public MyErrorListener(Context context){
this.context = context;
}
/**
* Handle the preparation to show the errors
*/
public void responsePreparation(){
}
/**
* Handle the client Errors
* #param error
*/
public void clientErrors(int error){
switch(error) {
case 400:
errorText.setText(context.getResources().getString(R.string.error_400_registration));
AlertDialog.Builder error400 = new AlertDialog.Builder(context);
error400.setTitle(context.getResources().getString(R.string.error_400_title_registration));
error400.setView(errorView);
error400.create();
error400.show();
break;
case 401:
errorText.setText(context.getResources().getString(R.string.error_401_registration));
AlertDialog.Builder error401 = new AlertDialog.Builder(context);
error401.setTitle(context.getString(R.string.error_401_title_registration));
error401.setView(errorView);
error401.create();
error401.show();
break;
}
}
#Override
public void onErrorResponse(VolleyError error) {
NetworkResponse response = error.networkResponse;
errorView = View.inflate(context, R.layout.error_dialog, null);
errorText = (TextView) errorView.findViewById(R.id.error_dialog_text);
responsePreparation();
if(response != null && response.data != null){
Log.v("Status code", String.valueOf(error.networkResponse.statusCode));
switch(response.statusCode){
case 400:
case 401:
clientErrors(response.statusCode);
break;
case 500:
errorText.setText(context.getString(R.string.error_502));
AlertDialog.Builder error500 = new AlertDialog.Builder(context);
error500.setTitle(context.getResources().getString(R.string.error_502_title));
error500.setView(errorView);
error500.create();
error500.show();
break;
case 502:
errorText.setText(context.getString(R.string.error_502));
AlertDialog.Builder error502 = new AlertDialog.Builder(context);
error502.setTitle(context.getResources().getString(R.string.error_502_title));
error502.setView(errorView);
error502.create();
error502.show();
break;
}
}
else{
if(checkConnection()){
errorText.setText(context.getResources().getString(R.string.error_502));
AlertDialog.Builder timeoutErrorServer = new AlertDialog.Builder(context);
timeoutErrorServer.setTitle(context.getResources().getString(R.string.error_502_title));
timeoutErrorServer.setView(errorView);
timeoutErrorServer.create();
timeoutErrorServer.show();
}
else{
errorText.setText(context.getResources().getString(R.string.timeout_error));
AlertDialog.Builder timeoutError = new AlertDialog.Builder(context);
timeoutError.setTitle(context.getResources().getString(R.string.timeout_error_title));
timeoutError.setView(errorView);
timeoutError.create();
timeoutError.show();
}
}
}
/**
* Gets the view of the dialog to show
* #return
*/
public View getDialogView(){
return errorView;
}
/**
* Gets the error text to show
* #return
*/
public TextView getErrorText(){
return errorText;
}
/**
* Checks if the user have got connection
* #return Boolean
*/
private Boolean checkConnection(){
ConnectivityManager conMgr = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo i = conMgr.getActiveNetworkInfo();
return !(i == null || !i.isConnected() || !i.isAvailable());
}
}
You can add to the switch the error codes that you want.
Then, when I want to make a new VolleyQuery, I override responsePreparation and clientErrors, like this example:
jsObjRequest = new JsonObjectRequest(Request.Method.POST, url, jsonBody, listener, new MyErrorListener(context){
#Override
public void responsePreparation() {
showProgress(false);
}
#Override
public void clientErrors(int error) {
Log.v("Error message", String.valueOf(error));
switch(error) {
case 400:
getErrorText().setText(context.getResources().getString(R.string.error_400_registration));
AlertDialog.Builder error400 = new AlertDialog.Builder(context);
error400.setTitle(context.getResources().getString(R.string.error_400_title_registration));
error400.setView(getDialogView());
error400.create();
error400.show();
break;
case 401:
getErrorText().setText(context.getResources().getString(R.string.error_401_registration));
AlertDialog.Builder error401 = new AlertDialog.Builder(context);
error401.setTitle(context.getString(R.string.error_401_title_registration));
error401.setView(getDialogView());
error401.create();
error401.show();
break;
}
}
});
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
if (error instanceof NetworkError) {
} else if (error instanceof ServerError) {
} else if (error instanceof AuthFailureError) {
} else if (error instanceof ParseError) {
} else if (error instanceof NoConnectionError) {
} else if (error instanceof TimeoutError) {
}
}
}