Retrofit call in non-activity class - android

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.

Related

Android convert Synchronous request to asynchronous using internal library Interface Callback

I am using .aar library in my app.
It has one Interface for the Network delegation which I need to overwrite.
ApiResponse executeApiCall(String url, HTTPMethod method, String params)
I am using Retrofit for the network calls. I need to convert the Synchronous call to USE asynchronous.
#Override
public ApiResponse executeApiCall(String url, HTTPMethod method, String params) {
ApiResponse apiResponse;
try {
// Synchronous Call
Call<String> call = RestClient.get().getStringResponse(url, method, params);
Response<String> response = call.execute();
apiResponse = new ApiResponse(response.code(), response.body());
} catch (Exception e) {
apiResponse = new ApiResponse();
}
return apiResponse;
}
Now I am stuck in how to use Asynchronous call within the Network Interface I must overwrite.
#Override
public ApiResponse executeApiCall(String url, HTTPMethod method, String params) {
ApiResponse apiResponse;
// Asynchronous Call
Call<String> call = RestClient.get().getStringResponse(url, method, params);
call.enqueue(new Callback<String>() {
#Override
public void onResponse(#NotNull Call<String> call, #NotNull Response<String> response) {
if (response.isSuccessful()) {
apiResponse = new ApiResponse(response.code(), response.body());
}
}
#Override
public void onFailure(#NotNull Call<String> call, #NotNull Throwable t) {
apiResponse = new ApiResponse();
}
});
return apiResponse;
}
I can not change the Network delegation interface. I must overwrite it and I need to use retrofit Asynchronous.
Your feedback is most appreciated. Thanks guys.
I found some code on a different stack overflow thread. Here is how you could use the code to achieve what you are looking for.
Here is the class
public abstract class AsyncRunnable<T> {
protected abstract void run(AtomicReference<T> notifier);
protected final void finish(AtomicReference<T> notifier, T result) {
synchronized (notifier) {
notifier.set(result);
notifier.notify();
}
}
public static <T> T wait(AsyncRunnable<T> runnable) {
final AtomicReference<T> notifier = new AtomicReference<>();
// run the asynchronous code
runnable.run(notifier);
// wait for the asynchronous code to finish
synchronized (notifier) {
while (notifier.get() == null) {
try {
notifier.wait();
} catch (InterruptedException ignore) {}
}
}
// return the result of the asynchronous code
return notifier.get();
}
}
You can use it like this
ApiResponse result = AsyncRunnable.wait(new AsyncRunnable<ApiResponse>() {
#Override
public void run(final AtomicReference<String> notifier) {
// here goes your async code, e.g.:
new Thread(new Runnable() {
#Override
public void run() {
Call<String> call =
RestClient.get().getStringResponse(url, method, params);
call.enqueue(new Callback<String>() {
#Override
public void onResponse(#NotNull Call<String> call,
#NotNull
Response<String> response) {
if (response.isSuccessful()) {
apiResponse = new
ApiResponse(response.code(),
response.body());
finish(notifier, apiResponse);
}
}
#Override
public void onFailure(#NotNull Call<String> call, #NotNull Throwable t) {
apiResponse = new ApiResponse();
finish(notifier, apiResponse);
}
});
}
}).start();
}
});
We wait for the response to come from the callback and notify the AsyncRunnable class.
I feel you, I had a similar problem with a library that needed to do heavy work (non-UI) on the UI thread. Yep, sounds like a psycopath's lib. Coroutines runBlocking are your friend. I realize that you're asking for a rx-java solution, but can't beat coroutines for this.
/**
* You can edit, run, and share this code.
* play.kotlinlang.org
*/
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
fun main() {
val apiResponse = executeApiCall()
println(apiResponse) // prints "response"
println("goodbye, world!!!")
}
/**
* Synchronous function that waits for coroutine to finish
*/
fun executeApiCall():String {
return runBlocking(Dispatchers.IO){
retrofitCall()
}
}
/**
* Method that invokes Retrofit call in the background.
*/
suspend fun retrofitCall():String{
// RestClient.get() ...
return "response"
}
Note that if you call executeApiCall from UI thread you still will block the UI thread. So possible you need something like lifecycleScope.launch{executeApiCall() } if you're inside an activity or fragment (gradle dependency androidx.lifecycle:lifecycle-runtime-ktx:2.1.0), or CoroutineScope(Dispatchers.IO).launch { executeApiCall() } otherwise.

how to deserialize json object and add response to arraylist

I am creating an app using soundcloud api but I am getting error while parsing json object, I am new in this things so don't know what I am doing wrong here
Here is my interface
ScService.java
public interface SCService
{
#GET("/resolve.json?url=https://m.soundcloud.com/kshmr/sets/materia&client_id=iZIs9mchVcX5lhVRyQGGAYlNPVldzAoX")
Call<Track> getTrack();
}
Here is my model class
Track.java
public class Track
{
#SerializedName("title")
private String mTitle;
#SerializedName("stream_url")
private String mStreamUrl;
public String getTitle()
{
return mTitle;
}
public String getStreamUrl()
{
return mStreamUrl;
}
}
MainActivity.class
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(Config.API_URL)
.addConverterFactory(GsonConverterFactory.create()).build();
SCService Scservice = retrofit.create(SCService.class);
Call<Track> call = Scservice.getTrack();
call.enqueue(new Callback<Track>(){
#Override
public void onResponse(Call<Track> call, Response<Track> response)
{
// TODO: Implement this method
if(response.isSuccessful())
{
//String track = response.body().toString();
//Log.e("jsonres",track);
//gson = new GsonBuilder().create();
gson = new Gson();
Track track = gson.fromJson(response.body().toString(), Track.class);
}
#Override
public void onFailure(Call p1, Throwable p2)
{
// TODO: Implement this method
}
});
}
Here is the JSON response from api callcall
enter code here
{"kind":"track","id":399448641,"created_at":"2018/02/14 11:40:02 +0000","user_id":319295181,"duration":188726,"commentable":true,"state":"finished","original_content_size":33279566,"last_modified":"2018/03/10 17:33:18 +0000","sharing":"public","tag_list":"KSHMR \"House of Cards\" \"Sidnie Tipton\" Dharma \"Spinnin' \"","permalink":"houseofcards-mixmaster-05b","streamable":true,"embeddable_by":"all","purchase_url":"http://www.spinninrecords.com/releases/house-of-cards","purchase_title":"Download/Stream","label_id":null,"genre":"Dance & EDM","title":"KSHMR - House of Cards (Ft. Sidnie Tipton)","description":"KSHMR and Sidnie Tipton team up again, this time for the bittersweet sound of \"House of Cards\" \n\nDownload / Stream here: https://www.spinninrecords.com/releases/house-of-cards/","label_name":null,"release":null,"track_type":null,"key_signature":null,"isrc":null,"video_url":null,"bpm":null,"release_year":null,"release_month":null,"release_day":null,"original_format":"wav","license":"all-rights-reserved","uri":"https://api.soundcloud.com/tracks/399448641","user":{"id":319295181,"kind":"user","permalink":"dharmaworldwide","username":"Dharma Worldwide","last_modified":"2018/03/09 12:08:27 +0000","uri":"https://api.soundcloud.com/users/319295181","permalink_url":"http://soundcloud.com/dharmaworldwide","avatar_url":"https://i1.sndcdn.com/avatars-000324744374-jdrkyv-large.jpg"},"permalink_url":"https://soundcloud.com/dharmaworldwide/houseofcards-mixmaster-05b","artwork_url":"https://i1.sndcdn.com/artworks-000302088414-recq7g-large.jpg","stream_url":"https://api.soundcloud.com/tracks/399448641/stream","download_url":"https://api.soundcloud.com/tracks/399448641/download","playback_count":135077,"download_count":0,"favoritings_count":7351,"reposts_count":1354,"comment_count":120,"downloadable":false,"waveform_url":"https://w1.sndcdn.com/0Bcy6WpC8dzY_m.png","attachments_uri":"https://api.soundcloud.com/tracks/399448641/attachments","policy":"ALLOW","monetization_model":"NOT_APPLICABLE"}
I can't use gson.fromJson(...) method, how could I fix this?
Ps-I have pretty much changed my code.
You should do:
EDIT:
ScService.java
public interface SCService
{
#GET("users/17586135/tracks?client_id=iZIs9mchVcX5lhVRyQGGAYlNPVldzAoX")
Call<Track> getTrack();
}
MainActivity.class
Call<Track> call = Scservice.getTracks();
call.enqueue(new Callback<Track>(){
#Override
public void onResponse(Call call, Response<Track> response)
{
// Get the result
Track track = response.body();
}
#Override
public void onFailure(Call p1, Throwable p2)
{
// TODO: Implement this method
}
});
}
More here
The Gson object should be used in this way:
gson = new GsonBuilder().create();
Track track = gson.fromJson(response.body().toString(),Track.class);

How to get different objects with Retrofit Android

I'm using Retrofit 1.9.0 in Android Studio to get a response from my REST API.
The method I want to do is GET, on this URL: http://dotfreeride.com/api/rest/adventures.php
I successfully retrieved the response for another API but that had only 1 object, this has 3 big objects.
My IApiMethods interface is like this:
#GET("/adventures.php")
JSONObject getAdventures(
Callback<AdventuresApi> cb
);
My AdventuresApi (Model class) is like this:
public class AdventuresApi {
public String adventure_id;
public String trimaps_context;
public String name;
public String video_url;
public List<ArrayPoi> array_poi;
public class ArrayPoi {
String poi_id;
String name;
String lat;
String lng;
String video_url;
}
}
My Retrofit call in the Activity is like this:
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint(API_URL)
.build();
IApiMethods methods = restAdapter.create(IApiMethods.class);
Callback callback = new Callback() {
#Override
public void success(Object o, Response response) {
}
#Override
public void failure(RetrofitError error) {
Log.e("JSON", "NO DATA!");
}
};
methods.getAdventures(callback);
I don't really know how to get the objects, I want to get the name of the object (Example: where trimaps_context is "verb", I need the name "Powder Hound")
For a single object I successfully did it like this in onResponse(Object o, Response response):
(ProfileApi) profileData = (ProfileApi) o;
Log.e("JSON", profileData.name + " " + profileData.email);
1) You are trying to combine both synchronous and asynchronous call. If you want to perform a request asynchronously you have to define it like this:
#GET("/adventures.php")
void getAdventures(
Callback<List<AdventuresApi>> cb
);
2) Do not create RestAdapter instance everytime you call request. It's really heavyweight operation. Use singleton pattern. You can then simply call:
ApiManager.getAdapter().getAdventures(...);
3) Object mapping is provided by parametrized Callback class:
ApiManager.getAdapter().getAdventures(
new Callback<List<AdventuresApi>>() {
#Override
public void success(List<AdventuresApi> adventures, Response response) {
// here you can access the adventures list
}
#Override
public void failure(RetrofitError error) {
// handle error
}
});

Retrofit + Okhttp cancel operation not working

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.

Retrofit and Centralized Error Handling

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

Categories

Resources