I am using retrofit in my application like this
final OkHttpClient okHttpClient = new OkHttpClient();
okHttpClient.interceptors().add(new YourInterceptor());
final OkClient okClient = new OkClient(okHttpClient);
Builder restAdapterBuilder = new RestAdapter.Builder();
restAdapterBuilder.setClient(okClient).setLogLevel(LogLevel.FULL)
.setEndpoint("some url");
final RestAdapter restAdapter = restAdapterBuilder.build();
public class YourInterceptor implements Interceptor {
#Override
public Response intercept(Chain chain) throws IOException {
// TODO Auto-generated method stub
Request request = chain.request();
if (request != null) {
Request.Builder signedRequestBuilder = request.newBuilder();
signedRequestBuilder.tag("taggiventorequest");
request = signedRequestBuilder.build();
request.tag();
}
return chain.proceed(request);
}
}
after sending request i am calling
okHttpClient.cancel("taggiventorequest");
but request is not cancelling i am getting the response from retrofit
dont know why it is not cancelling my request
I need volley like cancelation retrofit
As Retrofit API Spec, Canceling request will be included in version 2.0.
cancel() is a no-op after the response has been received. In all other
cases the method will set any callbacks to null (thus freeing strong
references to the enclosing class if declared anonymously) and render
the request object dead. All future interactions with the request
object will throw an exception. If the request is waiting in the
executor its Future will be cancelled so that it is never invoked.
For now, you can do it by creating custom callback class which implements on Callback from retrofit.
public abstract class CancelableCallback<T> implements Callback<T> {
private boolean canceled;
private T pendingT;
private Response pendingResponse;
private RetrofitError pendingError;
public CancelableCallback() {
this.canceled = false;
}
public void cancel(boolean remove) {
canceled = true;
}
#Override
public void success(T t, Response response) {
if (canceled) {
return;
}
onSuccess(t, response);
}
#Override
public void failure(RetrofitError error) {
if (canceled) {
return;
}
onFailure(error);
}
protected abstract void onSuccess(T t, Response response);
protected abstract void onFailure(RetrofitError error);
}
MyApi.java,
private interface MyApi {
#GET("/")
void getStringList(Callback<List<String>> callback);
}
In Activity or Fragment,
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint(Config.URL)
.build();
MyApi service = restAdapter.create(MyApi.class);
CancelableCallback callback = new CancelableCallback<List<String>>() {
#Override
protected void onSuccess(List<String> stringList, Response response) {
for (String str : stringList) {
Log.i("Result : ", str);
}
}
#Override
protected void onFailure(RetrofitError error) {
Log.e("Error : ", error.getMessage() + "");
}
};
service.getStringList(callback);
To cancel your request, simple call
callback.cancel();
This is an simple example to cancel each request. You can handle (cancel, pause, resume) two or more request at the same time by creating callback manager class. Please take a look that comment for reference.
Hope it will be useful for you.
The problem is that the request is completed, from the docs:
http://square.github.io/okhttp/javadoc/com/squareup/okhttp/OkHttpClient.html#cancel-java.lang.Object-
cancel
public OkHttpClient cancel(Object tag)
Cancels all scheduled or in-flight calls tagged with tag. Requests that are already complete cannot be canceled.
Related
I am beginner to android, am using Retrofit 2.0 for http calls. Since, I use same call in different activities, I created the function in non-activity class.
Here my retrofit calls in non-activity class code,
public class ServerRequests {
private static ServerRequests serverRequests = new ServerRequests();
public static ServerRequests getInstance(){
return serverRequests;
}
public LoginResponse ClientLogin(final LoginRequest request, Context context){
final ProgressDialog dialog = DialogueUtils.getInstance().showProgressDialog(context);
dialog.setMessage("Loading...");
dialog.show();
RestApi mApi = RetrofitProvider.getInstance().getRestApi();
Call<UserToken> call = mApi.clientLogin(request);
// Stores Login response
final LoginResponse loginResponse = new LoginResponse();
call.enqueue(new Callback<UserToken>() {
#Override
public void onResponse(Call<UserToken> call, Response<UserToken> response) {
dialog.dismiss();
if (response.isSuccessful()){
loginResponse.setMloginstatus(true);
loginResponse.setStatusCode(response.code());
loginResponse.setUserToken(response.body());
return;
}
// response isn't successful
loginResponse.setMloginstatus(false);
loginResponse.setStatusCode(response.code());
loginResponse.setMessage(response.message());
loginResponse.setUserToken(null);
}
#Override
public void onFailure(Call<UserToken> call, Throwable t) {
dialog.dismiss();
loginResponse.setMloginstatus(false);
loginResponse.setUserToken(null);
loginResponse.setMessage(t.getMessage());
loginResponse.setStatusCode(FAILURE_ERROR);
}
});
return loginResponse;
}
}
I will make call to above function in Activity class,
Here is the code,
LoginRequest request = new LoginRequest();
request.setPassword(PASSWORD);
request.setEmail(USER_NAME);
// Calling Login function
LoginResponse response = ServerRequests.getInstance().ClientLogin(request, this);
Here, before receiving response in activity class, next set of code lines are executing. So, Activity class doesn't waiting till function to return back to the calling sequence.
Can anyone please suggest me what's the better approach to do this.
Thank you
I think you can use a interface to handle callback.
Step1: Define an interface
public interface LoginListener{
public void success(Response<UserToken> response);
public void failed(String message);
}
Step2: Use this interface on the method that you use retrofit.
public LoginResponse ClientLogin(final LoginRequest request, Context context, LoginListener listener){
final ProgressDialog dialog = DialogueUtils.getInstance().showProgressDialog(context);
dialog.setMessage("Loading...");
dialog.show();
RestApi mApi = RetrofitProvider.getInstance().getRestApi();
Call<UserToken> call = mApi.clientLogin(request);
// Stores Login response
final LoginResponse loginResponse = new LoginResponse();
call.enqueue(new Callback<UserToken>() {
#Override
public void onResponse(Call<UserToken> call, Response<UserToken> response) {
dialog.dismiss();
listener.success(response);
}
#Override
public void onFailure(Call<UserToken> call, Throwable t) {
dialog.dismiss();
listener.failed("message error");
}
});
return loginResponse;
}
I hope, This way can help you.
You can't return the result from your method, because you're making the Retrofit call asynchronously. Use a callback instead.
public interface GenericCallback<T> {
void success(T result);
void failure(... whatever you need);
}
public void ClientLogin(final LoginRequest request, Context context, final GenericCallback<LoginResponse> callback){
final ProgressDialog dialog = DialogueUtils.getInstance().showProgressDialog(context);
dialog.setMessage("Loading...");
dialog.show();
RestApi mApi = RetrofitProvider.getInstance().getRestApi();
Call<UserToken> call = mApi.clientLogin(request);
// Stores Login response
final LoginResponse loginResponse = new LoginResponse();
call.enqueue(new Callback<UserToken>() {
#Override
public void onResponse(Call<UserToken> call, Response<UserToken> response) {
dialog.dismiss();
if (response.isSuccessful()){
loginResponse.setMloginstatus(true);
loginResponse.setStatusCode(response.code());
loginResponse.setUserToken(response.body());
callback.success(loginResponse);
return;
}
// response isn't successful
loginResponse.setMloginstatus(false);
loginResponse.setStatusCode(response.code());
loginResponse.setMessage(response.message());
loginResponse.setUserToken(null);
callback.failure(...);
}
#Override
public void onFailure(Call<UserToken> call, Throwable t) {
dialog.dismiss();
loginResponse.setMloginstatus(false);
loginResponse.setUserToken(null);
loginResponse.setMessage(t.getMessage());
loginResponse.setStatusCode(FAILURE_ERROR);
callback.failure(...);
}
});
}
call.enqueue() is asynchronous.. the call is done in the background, and then, as soon as the call finishes, one of the two callbacks is called, either onResponse or onFailure.
call.execute() is synchronous, it will block the execution until the call finishes, but in that case you need to handle threading manually (remember that in android you cannot block the UI thread).
I recommend enqueue.
I am new to RxJava so please forgive me if this sounds too newbie :-).
As of now I have an abstract CallbackClass that implements the Retofit Callback. There I catch the Callback's "onResponse" and "onError" methods and handle various error types before finally forwarding to the custom implemented methods.
I also use this centralized class to for request/response app logging and other stuff.
For example: for specific error codes from my sever I receive a new Auth token in the response body, refresh the token and then clone.enqueue the call.
There are of course several other global behaviors to the responses from my server.
Current solution (Without Rx):
public abstract void onResponse(Call<T> call, Response<T> response, boolean isSuccess);
public abstract void onFailure(Call<T> call, Response<T> response, Throwable t, boolean isTimeout);
#Override
public void onResponse(Call<T> call, Response<T> response) {
if (_isCanceled) return;
if (response != null && !response.isSuccessful()) {
if (response.code() == "SomeCode" && retryCount < RETRY_LIMIT) {
TokenResponseModel newToken = null;
try {
newToken = new Gson().fromJson(new String(response.errorBody().bytes(), "UTF-8"), TokenResponseModel.class);
} catch (Exception e) {
e.printStackTrace();
}
SomeClass.token = newToken.token;
retryCount++;
call.clone().enqueue(this);
return;
}
}
} else {
onResponse(call, response, true);
removeFinishedRequest();
return;
}
onFailure(call, response, null, false);
removeFinishedRequest();
}
#Override
public void onFailure(Call<T> call, Throwable t) {
if (_isCanceled) return;
if (t instanceof UnknownHostException)
if (eventBus != null)
eventBus.post(new NoConnectionErrorEvent());
onFailure(call, null, t, false);
removeFinishedRequest();
}
My question is: Is there any way to have this sort of centralized response handling behavior before finally chaining (or retrying) back to the subscriber methods?
I found these 2 links which both have a nice starting point but not a concrete solution. Any help will be really appreciated.
Forcing request retry after custom API exceptions in RxJava
Retrofit 2 and RxJava error handling operators
Two links you provided are a really good starting point, which I used to construct solution to react to accidental
network errors happen sometimes due to temporary lack of network connection, or switch to low throughtput network standard, like EDGE, which causes SocketTimeoutException
server errors -> happen sometimes due to server overload
I have overriden CallAdapter.Factory to handle errors and react appropriately to them.
Import RetryWithDelayIf from the solution you found
Override CallAdapter.Factory to handle errors:
public class RxCallAdapterFactoryWithErrorHandling extends CallAdapter.Factory {
private final RxJavaCallAdapterFactory original;
public RxCallAdapterFactoryWithErrorHandling() {
original = RxJavaCallAdapterFactory.create();
}
#Override
public CallAdapter<?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
return new RxCallAdapterWrapper(retrofit, original.get(returnType, annotations, retrofit));
}
public class RxCallAdapterWrapper implements CallAdapter<Observable<?>> {
private final Retrofit retrofit;
private final CallAdapter<?> wrapped;
public RxCallAdapterWrapper(Retrofit retrofit, CallAdapter<?> wrapped) {
this.retrofit = retrofit;
this.wrapped = wrapped;
}
#Override
public Type responseType() {
return wrapped.responseType();
}
#SuppressWarnings("unchecked")
#Override
public <R> Observable<?> adapt(final Call<R> call) {
return ((Observable) wrapped.adapt(call)).onErrorResumeNext(new Func1<Throwable, Observable>() {
#Override
public Observable call(Throwable throwable) {
Throwable returnThrowable = throwable;
if (throwable instanceof HttpException) {
HttpException httpException = (HttpException) throwable;
returnThrowable = httpException;
int responseCode = httpException.response().code();
if (NetworkUtils.isClientError(responseCode)) {
returnThrowable = new HttpClientException(throwable);
}
if (NetworkUtils.isServerError(responseCode)) {
returnThrowable = new HttpServerException(throwable);
}
}
if (throwable instanceof UnknownHostException) {
returnThrowable = throwable;
}
return Observable.error(returnThrowable);
}
}).retryWhen(new RetryWithDelayIf(3, DateUtils.SECOND_IN_MILLIS, new Func1<Throwable, Boolean>() {
#Override
public Boolean call(Throwable throwable) {
return throwable instanceof HttpServerException
|| throwable instanceof SocketTimeoutException
|| throwable instanceof UnknownHostException;
}
}));
}
}
}
HttpServerException is just a custom exception.
Use it in Retrofit.Builder
Retrofit retrofit = new Retrofit.Builder()
.addCallAdapterFactory(new RxCallAdapterFactoryWithErrorHandling())
.build();
Extra: If you wish to parse errors that come from API (error that don't invoke UnknownHostException, HttpException or MalformedJsonException or etc.) you need to override Factory and use custom one during building Retrofit instance. Parse the response and check if it contains errors. If yes, then throw error and error will be handled inside the method above.
have you consider using the rxjava adapter for retrofit?
https://mvnrepository.com/artifact/com.squareup.retrofit2/adapter-rxjava/2.1.0
in your gradle file add
compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0'
here's a interface for retrofit
public interface Service {
#GET("userauth/login?")
Observable<LoginResponse> getLogin(
#Query("v") String version,
#Query("username") String username,
#Query("password") String password);
}
and here's my implementation
Service.getLogin(
VERSION,
"username",
"password")
.subscribe(new Subscriber<LoginResponse>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(LoginResponse loginResponse) {
}
});
please note I'm using the gson converter factory to parse my response so I get an pojo (Plain Ole Java Object) returned.
See how you can do it.
Here is api call and pass Request model and response model in this.
public interface RestService {
//SEARCH_USER
#POST(SEARCH_USER_API_LINK)
Observable<SearchUserResponse> getSearchUser(#Body SearchUserRequest getSearchUserRequest);
}
This is the retrofit call,I used retrofit2
public RestService getRestService() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(ApiConstants.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.client(getOkHttpClient())
.build();
return retrofit.create(RestService.class);
}
//get OkHttp instance
#Singleton
#Provides
public OkHttpClient getOkHttpClient() {
HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor();
httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.interceptors().add(httpLoggingInterceptor);
builder.readTimeout(60, TimeUnit.SECONDS);
builder.connectTimeout(60, TimeUnit.SECONDS);
return builder.build();
}
This is the api call, call it in your activity.
#Inject
Scheduler mMainThread;
#Inject
Scheduler mNewThread;
//getSearchUser api method
public void getSearchUser(String user_id, String username) {
SearchUserRequest searchUserRequest = new SearchUserRequest(user_id, username);
mObjectRestService.getSearchUser(searchUserRequest).
subscribeOn(mNewThread).
observeOn(mMainThread).
subscribe(searchUserResponse -> {
Timber.e("searchUserResponse :" + searchUserResponse.getResponse().getResult());
if (isViewAttached()) {
getMvpView().hideProgress();
if (searchUserResponse.getResponse().getResult() == ApiConstants.STATUS_SUCCESS) {
} else {
}
}
}, throwable -> {
if (isViewAttached()) {
}
});
}
Hope this will help you.
I've created a method to check if my app is able to connect to my server using OkHttp.
This is my test class:
public class NetworkTest {
static boolean resultWeb = false;
public static boolean pingTestWeb() {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("http://www.google.com")//My server address will go here
.build();
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Request request, IOException e) {
resultWeb = false;
Log.i("Error","Failed to connect: "+e.getMessage());
}
#Override
public void onResponse(Response response) throws IOException {
Log.i("Success","Success: "+response.code());
if (response.code() == 200) {
resulWeb = true;
}
}
});
return resultWeb;
}
And here is where I'm making the test on my activity on OnCreate():
if (NetworkTest.pingTestWeb()) {
// Do something if true...
} else {
// Do something if false, like showing an AlertDialog...
}
The question is, my pingTestWeb have the default Timeout time of 10000ms, how do I make the activity create the AlertDialog only if the pingTestWeb is false? Because it isn't waiting for the response.
You can also use CountDownLatch, in this way you can put async in your test:
public class NetworkTest {
static boolean resultWeb = false;
public static boolean pingTestWeb() {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("http://www.google.com")//My server address will go here
.build();
CountDownLatch countDownLatch = new CountDownLatch(1);
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Request request, IOException e) {
resultWeb = false;
Log.i("Error","Failed to connect: "+e.getMessage());
countDownLatch.countDown();
}
#Override
public void onResponse(Response response) throws IOException {
Log.i("Success","Success: "+response.code());
if (response.code() == 200) {
resulWeb = true;
}
countDownLatch.countDown();
}
});
countDownLatch.await();
return resultWeb;
}
I think what may be happening here is you're performing the OkHttp call asynchronously, so you're hitting the return statement before the task is complete. For the sake of the test would it be possible to do the OkHttp call synchronously? You can handle the success/failure case with response.isSuccessful seen below.
private final OkHttpClient client = new OkHttpClient();
public void run() throws Exception {
Request request = new Request.Builder()
.url("your_url_here")
.build();
Response response = client.newCall(request).execute();
if(response.isSuccessful()){
return true;
}else return false;
}
Each request to the server may return error_code. I want to handle these error in one place
when I was using AsyncTask I had a BaseAsyncTask like that
public abstract class BaseAsyncTask<Params, Progress, Result> extends AsyncTask<Params, Progress, Result> {
protected Context context;
private ProgressDialog progressDialog;
private Result result;
protected BaseAsyncTask(Context context, ProgressDialog progressDialog) {
this.context = context;
this.progressDialog = progressDialog;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected void onPostExecute(Result result) {
super.onPostExecute(result);
HttpResponse<ErrorResponse> response = (HttpResponse<ErrorResponse>) result;
if(response.getData().getErrorCode() != -1) {
handleErrors(response.getData());
}else
onResult(result);
}
private void handleErrors(ErrorResponse errorResponse) {
}
public abstract void onResult(Result result);
}
But, using retrofit each request has its error handling callback:
git.getFeed(user,new Callback<gitmodel>() {
#Override
public void success(gitmodel gitmodel, Response response) {
}
#Override
public void failure(RetrofitError error) {
}
});
}
});
How can I handle all errors in one place?
If you need to get some 'logic' error, then you need some Java logic since it's not a Retrofit feature so basically:
Create a Your implementation Callback that implements the Retrofit Callback
Create a base object that define the method 'isError'
Modify Retrofit RestAdapter in order to get your Callback instead of the Retrofit One
MyCallback.java
import android.util.Log;
import retrofit.Callback;
import retrofit.client.Response;
public abstract class MyCallback<T extends MyObject> implements Callback<T> {
#Override
public final void success(T o, Response response) {
if (o.isError()) {
// [..do something with error]
handleLogicError(o);
}
else {
handleSuccess(o, response);
}
}
abstract void handleSuccess(T o, Response response);
void handleLogicError(T o) {
Log.v("TAG", "Error because userId is " + o.id);
}
}
MyObject.java (the base class for all your objects you get from Retrofit)
public class MyObject {
public long id;
public boolean isError() {
return id == 1;
}
}
MyRealObject.java - a class that extends the base object
public class MyRealObject extends MyObject {
public long userId;
public String title;
public String body;
}
RetroInterface.java - the interface used by retrofit you should be familiar with
import retrofit.http.GET;
import retrofit.http.Path;
public interface RetroInterface {
#GET("/posts/{id}")
void sendGet(#Path("id") int id, MyCallback<MyRealObject> callback);
}
And finally the piece of code where you use all the logic
RestAdapter adapter = new RestAdapter.Builder()
.setEndpoint("http://jsonplaceholder.typicode.com")
.build();
RetroInterface itf = adapter.create(RetroInterface.class);
itf.sendGet(2, new MyCallback<MyRealObject>() {
#Override
void handleSuccess(MyRealObject o, Response response) {
Log.v("TAG", "success");
}
#Override
public void failure(RetrofitError error) {
Log.v("TAG", "failure");
}
});
If you copy and paste this code, you'll get an error when you'll execute the itf.sendGet(1, new MyCallback..) and a success for itf.sendGet(2, new MyCallback...)
Not sure I understood it correctly, but you could create one Callback and pass it as a parameter to all of your requests.
Instead of:
git.getFeed(user,new Callback<gitmodel>() {
#Override
public void success(gitmodel gitmodel, Response response) {
}
#Override
public void failure(RetrofitError error) {
}
});
First define your Callback:
Callback<gitmodel> mCallback = new Callback<gitmodel>() {
#Override
public void success(gitmodel gitmodel, Response response) {
}
#Override
public void failure(RetrofitError error) {
// logic to handle error for all requests
}
};
Then:
git.getFeed(user, mCallback);
In Retrofit you can specify ErrorHandler to all requests.
public class ApiErrorHandler implements ErrorHandler {
#Override
public Throwable handleError(RetrofitError cause) {
//here place your logic for all errors
return cause;
}
}
Apply it to RestAdapter
RestAdapter.Builder()
.setClient(client)
.setEndpoint(endpoint)
.setErrorHandler(errorHandler)
.build();
I think that it is what you asked for.
In Retrofit2 you can't set an ErrorHandler with the method .setErrorHandler(), but you can create an interceptor to fork all possible errors centralised in one place of your application.
With this example you have one centralised place for your error handling with Retrofit2 and OkHttpClient. Just reuse the Retrofit object (retrofit).
You can try this standalone example with a custom interceptor for network and server errors. These both will be handled differently in Retrofit2, so you have to check the returned error code from the server over the response code (response.code()) and if the response was not successful (!response.isSuccessful()).
For the case that the user has no connection to the network or the server you have to catch an IOException of the method Response response = chain.proceed(chain.request()); and handle the network error in the catch block.
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(loggingInterceptor)
.addInterceptor(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
try {
Response response = chain.proceed(chain.request());
if (!response.isSuccessful()) {
Log.e("tag", "Failure central - response code: " + response.code());
Log.e("tag", "central server error handling");
// Central error handling for error responses here:
// e.g. 4XX and 5XX errors
switch (response.code()) {
case 401:
// do something when 401 Unauthorized happened
// e.g. delete credentials and forward to login screen
// ...
break;
case 403:
// do something when 403 Forbidden happened
// e.g. delete credentials and forward to login screen
// ...
break;
default:
Log.e("tag", "Log error or do something else with error code:" + response.code());
break;
}
}
return response;
} catch (IOException e) {
// Central error handling for network errors here:
// e.g. no connection to internet / to server
Log.e("tag", e.getMessage(), e);
Log.e("tag", "central network error handling");
throw e;
}
}
})
.build();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://10.0.2.2:8000/api/v1/")
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build();
UserRepository backendRepository = retrofit.create(UserRepository.class);
backendRepository.getUser("userId123").enqueue(new Callback<UserModel>() {
#Override
public void onResponse(Call<UserModel> call, retrofit2.Response<UserModel> response) {
Log.d("tag", "onResponse");
if (!response.isSuccessful()) {
Log.e("tag", "onFailure local server error handling code:" + response.code());
} else {
// its all fine with the request
}
}
#Override
public void onFailure(Call<UserModel> call, Throwable t) {
Log.e("tag", "onFailure local network error handling");
Log.e("tag", t.getMessage(), t);
}
});
UserRepository example:
public interface UserRepository {
#GET("users/{userId}/")
Call<UserModel> getUser(#Path("userId") String userId);
}
UserModel example:
public class UserModel implements Parcelable {
#SerializedName("id")
#Expose
public String id = "";
#SerializedName("email")
#Expose
public String mail = "";
public UserModel() {
}
protected UserModel(Parcel in) {
id = in.readString();
mail = in.readString();
}
public static final Creator<UserModel> CREATOR = new Creator<UserModel>() {
#Override
public UserModel createFromParcel(Parcel in) {
return new UserModel(in);
}
#Override
public UserModel[] newArray(int size) {
return new UserModel[size];
}
};
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(id);
dest.writeString(mail);
}
}
Fairly simply Retrofit custom error handling example. Is set up so that you don't need to do much work in the 'failure' handler of a retrofit call to get the user-visible error message to show. Works on all endpoints. There's lots of exception handling as our server folks like to keep us on our toes by sending all kinds of random stuff..!
// on error the server sends JSON
/*
{ "error": { "data": { "message":"A thing went wrong" } } }
*/
// create model classes..
public class ErrorResponse {
Error error;
public static class Error {
Data data;
public static class Data {
String message;
}
}
}
//
/**
* Converts the complex error structure into a single string you can get with error.getLocalizedMessage() in Retrofit error handlers.
* Also deals with there being no network available
*
* Uses a few string IDs for user-visible error messages
*/
private static class CustomErrorHandler implements ErrorHandler {
private final Context ctx;
public CustomErrorHandler(Context ctx) {
this.ctx = ctx;
}
#Override
public Throwable handleError(RetrofitError cause) {
String errorDescription;
if (cause.isNetworkError()) {
errorDescription = ctx.getString(R.string.error_network);
} else {
if (cause.getResponse() == null) {
errorDescription = ctx.getString(R.string.error_no_response);
} else {
// Error message handling - return a simple error to Retrofit handlers..
try {
ErrorResponse errorResponse = (ErrorResponse) cause.getBodyAs(ErrorResponse.class);
errorDescription = errorResponse.error.data.message;
} catch (Exception ex) {
try {
errorDescription = ctx.getString(R.string.error_network_http_error, cause.getResponse().getStatus());
} catch (Exception ex2) {
Log.e(TAG, "handleError: " + ex2.getLocalizedMessage());
errorDescription = ctx.getString(R.string.error_unknown);
}
}
}
}
return new Exception(errorDescription);
}
}
// When creating the Server...
retrofit.RestAdapter restAdapter = new retrofit.RestAdapter.Builder()
.setEndpoint(apiUrl)
.setLogLevel(retrofit.RestAdapter.LogLevel.FULL)
.setErrorHandler(new CustomErrorHandler(ctx)) // use error handler..
.build();
server = restAdapter.create(Server.class);
// Now when calling server methods, get simple error out like this:
server.postSignIn(login,new Callback<HomePageResponse>(){
#Override
public void success(HomePageResponse homePageResponse,Response response){
// Do success things!
}
#Override
public void failure(RetrofitError error){
error.getLocalizedMessage(); // <-- this is the message to show to user.
}
});
I'm trying to convert my app which currently uses Retrofit, to use RX Java.
In order to handle Pagination, I traditionally was grabbing the nextPage URL from the response headers.
#Override
public void success(Assignment assignment, Response response) {
response.getHeaders(); // Do stuff with header info
}
However, since switching to RX Java, i'm not sure how to get the response information from my retrofit call.
#GET("/{item_id}/users")
Observable<List<Objects>> getObjects(#Path("object_id") long object_id);
#GET("/{next}")
Observable<List<Objects>> getNextPageObjects(#Path("next") String nextURL);
Is there a way to have my retrofit calls to return my header information along with my Typed objects?
You can use
Observable<Response>
as return type to get the response details
#GET("/{item_id}/users")
Observable<Response> getObjects(#Path("object_id") long object_id);
#GET("/{next}")
Observable<Response>getNextPageObjects(#Path("next") String nextURL);
This is how the Response object would look like
You would have to then parse the headers and body from the observable
serviceClass.getNextPageObjects("next").flatMap(new Func1<Response, Observable<List<Objects>>() {
#Override
public Observable<AuthState> call(Response response) {
List<Header> headers = response.getHeaders();
GsonConverter converter = new GsonConverter(new Gson());
// you would have to change this to convert the objects to list
List<Objects> list = converter.fromBody(response.getBody(),
YourClass.class);
return Observable.from(list);
}
}
Let me spoiler this. You could intercept request and response as of OkHttp-2.2. As on OkHttp wiki says interceptors will not work in Retrofit with OkUrlFactory. You need to provide your Client implementation to execute Retrofit requests on this custom Client and directly on OkHttp
Unfortunately, it is not out yet (soon).
public class ResponseHeaderInterceptor implements Interceptor {
public interface ResponseHeaderListener{
public void onHeadersIntercepted(Headers headers);
}
private ResponseHeaderListener mListener;
public ResponseHeaderInterceptor(){};
public ResponseHeaderInterceptor(ResponseHeaderListener listener){
mListener = listener;
}
#Override public Response intercept(Chain chain) throws IOException {
Response response = chain.proceed(chain.request());
if(mListener != null){
mListener.onHeadersIntercepted(response.headers());
}
return response;
}
Usage:
ResponseHeaderListener headerListener = new ResponseHeaderListener(){
#Override
public void onHeadersIntercepted(Headers headers){
//do stuff with headers
}
};
OkHttpClient okHttpClient = new OkHttpClient();
okHttpClient.interceptors().add(new ResponseHeaderInterceptor(headerListener));
RestAdapter.Builder restAdapterBuilder = new RestAdapter.Builder();
restAdapterBuilder.setClient(new OkHttpClient22(okHttpClient));
This is application interceptor and will be called only once.
Please note that I did all this by logic, I still don't have OkHttp-2.2. Just read about it here. I'll remove some of this text when 2.2 is latest jar.
Alternatively, you can try to create custom client and with one interface deliver the response:
public class InterceptableClient extends OkClient {
private ResponseListener mListener;
public interface ResponseListener{
public void onResponseIntercepted(Response response);
}
public InterceptableClient(){};
public InterceptableClient(ResponseListener listener){
mListener = listener;
}
#Override
public retrofit.client.Response execute(Request request) throws IOException {
Response response = super.execute(request);
if(mListener != null) //runs on the executor you have provided for http execution
mListener.onResponseIntercepted(response);
return response;
}
}
Edit: OkHttp 2.2 has been released.