Control errors on Retrofit calls like (404) - java

I want to get the HttpResponse code and control the errors for display them to the user.
I have my static retrofit request:
public class NetworkClient {
//KEY: f663e4c56cc039c837109c82c78bbd69
public static Retrofit getRetrofit(){
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.themoviedb.org/3/")
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
// .client(OkHttpClient())
.build();
return retrofit;
}
And observable who takes the request returns me an observable:
public Observer<Response> getObserver(){
Observer<Response> response = new Observer<Response>() {
#Override
public void onSubscribe(Disposable d) {
Log.d("test", "onSubscribe");
}
#Override
public void onNext(Response response) {
Log.d("test", "onNext");
fragmentInterface.showMovies(response);
}
#Override
public void onError(Throwable e) {
Log.d("test", "onError");
e.printStackTrace();
if(e instanceof HttpException){
int errorCode = ((HttpException) e).response().code();
}
}
#Override
public void onComplete() {
Log.d("test", "onComplete");
}
};
return response;
}
I see i can get the error from the onError, but i see i can have a class to get all type errors.

Related

cannot print data from response

I'm trying to get data from api
I did response model,and apiInterface ,ApiClient etc.
It's all good,and there are no code mistakes
,but the problem is that response body is empty.
In this example I decided to know the cause of the problem ,but i didn't know.
Api: http://api.serpstack.com/search?access_key=8cdb389dedab3a1b462de83a67921de2&query=+%22cart%22%20-intitle:%22profiles%22%20-inurl:%22dir/+%22+site:linkedin.com/in/+OR+site:linkedin.com/pub/
// Logcat : I/trace: []
//MainActivity :
public class MainActivity extends AppCompatActivity {
ApiClient apiClient;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
apiClient = new ApiClient().getInstance(MainActivity.this);
Call<Response>call = apiClient.sNewsApi.getResultSearch("8cdb389dedab3a1b462de83a67921de2","+%22cart%22%20-intitle:%22profiles%22%20-inurl:%22dir/+%22+site:linkedin.com/in/+OR+site:linkedin.com/pub/");
call.enqueue(new Callback<Response>(){
#Override
public void onResponse(Call<Response> call, retrofit2.Response<Response> response) {
if(response.isSuccessful()){
Log.i("trace"," "+response.body().getOrganicResults());
}else {
Log.i("trace","Not");
}
}
#Override
public void onFailure(Call<Response> call, Throwable t) {
Log.i("trace","Not"+t.getMessage());
}
});
}
}
// ApiInterface :
public interface ApiInterface {
#GET("search")
Call<Response>getResultSearch(#Query("access_key") String access_key,#Query("query") String query);
}
// ApiClient :
public class ApiClient {
public static final String NEWS_API_URL ="http://api.serpstack.com/";
private static final Object LOCK = new Object();
public static ApiInterface sNewsApi;
public static ApiClient sInstance;
public ApiClient() {
}
public static ApiClient getInstance(Context context) {
if (sInstance == null || sNewsApi == null) {
synchronized (LOCK) {
Cache cache = new Cache(context.getApplicationContext().getCacheDir(), 5 * 1024 * 1024);
Interceptor networkInterceptor = new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
CacheControl cacheControl = new CacheControl.Builder()
.maxAge(1, TimeUnit.HOURS)
.maxStale(3, TimeUnit.DAYS)
.build();
return chain.proceed(chain.request())
.newBuilder()
.header("Cache-Control", cacheControl.toString())
.build();
}
};
// For logging
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY);
// Building OkHttp client
OkHttpClient client = new OkHttpClient.Builder()
/// .cache(cache)
.addNetworkInterceptor(networkInterceptor)
.addInterceptor(loggingInterceptor)
.build();
// Configure GSON
Gson gson = new GsonBuilder().registerTypeAdapter(Date.class, new DateDeserializer()).create();
// Retrofit Builder
Retrofit.Builder builder = new Retrofit.Builder().
baseUrl(NEWS_API_URL).client(client).addConverterFactory(GsonConverterFactory.create(gson));
// Set NewsApi instance
sNewsApi = builder.build().create(ApiInterface.class);
sInstance = new ApiClient();
}
}
return sInstance;
}
}
// Response :
public class Response{
#SerializedName("request")
private Request request;
#SerializedName("pagination")
private Pagination pagination;
#SerializedName("organic_results")
private List<OrganicResultsItem> organicResults;
#SerializedName("search_information")
private SearchInformation searchInformation;
#SerializedName("search_parameters")
private SearchParameters searchParameters;
public Request getRequest(){
return request;
}
public Pagination getPagination(){
return pagination;
}
public List<OrganicResultsItem> getOrganicResults(){
return organicResults;
}
public SearchInformation getSearchInformation(){
return searchInformation;
}
public SearchParameters getSearchParameters(){
return searchParameters;
}
}
It's simple, just add this line:
System.out.println(response.body());
and this will show what you have in the response, make sure to put this inside
if (response.isSuccessful()) {
System.out.println(response.body());
Log.i("trace"," "+response.body().getOrganicResults());
} else {
Log.i("trace","Not");
}

Using OkHttp3 WebSocket with Retrofit to continuously track an android devices location

I have the following java code that I'd like to use in an android app to query an api for continuous lat/lng changes of a device that is running a client app, I want to track the device. I believe the WebSocketCall method I'm attempting to use is deprecated. From what I can tell, there's a problem with how I'm trying to use the webSocket call to create the retrofit client and enqueue the data from the WebSocketListner into retrofit. I've researched several WebSocketListener examples and being a total n00b, I haven't been able to figure out the code. My idea is to keep the connection open to the api via WebSocket and process the data response using retrofit. Any assistance would be greatly appreciated.
private WebSocketCall webSocket;
private void createWebSocket() {
final MainApplication application = (MainApplication) getActivity().getApplication();
application.getServiceAsync(new MainApplication.GetServiceCallback() {
#Override
public void onServiceReady(final OkHttpClient client, final Retrofit retrofit, WebService service) {
User user = application.getUser();
map.moveCamera(CameraUpdateFactory.newLatLngZoom(
new LatLng(user.getLatitude(), user.getLongitude()), user.getZoom()));
service.getDevices().enqueue(new WebServiceCallback<List<Device>>(getContext()) {
#Override
public void onSuccess(retrofit2.Response<List<Device>> response) {
for (Device device : response.body()) {
if (device != null) {
devices.put(device.getId(), device);
}
}
Request request = new Request.Builder().url(retrofit.baseUrl().url().toString() + "api/socket").build();
webSocket = WebSocketCall.create(client, request);
webSocket.enqueue(new WebSocketListener() {
#Override
public void onOpen(WebSocket webSocket, Response response) {
}
#Override
public void onFailure(IOException e, Response response) {
reconnectWebSocket();
}
#Override
public void onMessage(ResponseBody message) throws IOException {
final String data = message.string();
handler.post(new Runnable() {
#Override
public void run() {
try {
handleMessage(data);
} catch (IOException e) {
Log.w(MainFragment.class.getSimpleName(), e);
}
}
});
}
#Override
public void onClose(int code, String reason) {
reconnectWebSocket();
}
});
}
});
}
#Override
public boolean onFailure() {
return false;
}
});
}
So because I'm a total n00b it took some time and a lot of questions to figure this out. Maybe it'll help someone else in the future.
private WebSocket webSocket;
private void createWebSocket() {
final MainApplication application = (MainApplication) getActivity().getApplication();
application.getServiceAsync(new MainApplication.GetServiceCallback() {
#Override
public void onServiceReady(final OkHttpClient client, final Retrofit retrofit, WebService service) {
User user = application.getUser();
map.moveCamera(CameraUpdateFactory.newLatLngZoom(
new LatLng(user.getLatitude(), user.getLongitude()), user.getZoom()));
service.getDevices().enqueue(new WebServiceCallback<List<Device>>(getContext()) {
#Override
public void onSuccess(retrofit2.Response<List<Device>> response) {
for (Device device : response.body()) {
if (device != null) {
devices.put(device.getId(), device);
}
}
Request request = new Request.Builder().url(retrofit.baseUrl().url().toString() + "api/socket").build();
Log.e("WebSockets", "Headers: " + request.headers().toString());
WebSocketListener webSocketListener = new WebSocketListener() {
private static final int NORMAL_CLOSURE_STATUS = 1000;
#Override
public void onOpen(WebSocket webSocket, Response response) {
webSocket.send("{Auth-Token:secret-api-token-here}");
Log.e("WebSockets", "Connection accepted!");
}
#Override
public void onFailure(#NotNull WebSocket webSocket, #NotNull Throwable t, #Nullable Response response) {
reconnectWebSocket();
}
#Override
public void onMessage(#NotNull WebSocket webSocket, #NotNull String text) {
final String data = text;
Log.e("WebSockets", "Receiving : " + text);
handler.post(new Runnable() {
#Override
public void run() {
try {
handleMessage(data);
} catch (IOException e) {
Log.w(MainFragment.class.getSimpleName(), e);
}
}
});
}
#Override
public void onMessage(WebSocket webSocket, ByteString bytes) {
Log.e("WebSockets", "Receiving bytes : " + bytes.hex());
}
#Override
public void onClosing(WebSocket webSocket, int code, String reason) {
webSocket.close(NORMAL_CLOSURE_STATUS, null);
Log.e("WebSockets", "Closing : " + code + " / " + reason);
}
#Override
public void onClosed(#NotNull WebSocket webSocket, int code, #NotNull String reason) {
reconnectWebSocket();
}
};
webSocket = client.newWebSocket(request, webSocketListener);
}
});
}
#Override
public boolean onFailure() {
return false;
}
});
}

Best way to use retrofit response in several activies

I have a function searchForTrips() which sends an API request and fetch some response in following way.
private void searchForTrips(){
int departurePortId = PORT_ID_LIST.get(departurePort);
int returnPortId = PORT_ID_LIST.get(returnPort);
int pax= Integer.parseInt(noOfPassengers);
String departureDatePARSED = DEPARTURE_DATE_VALUES.get(departureDate);
String returnDatePARSED = RETURN_DATE_VALUES.get(departureDate);
Call<TripSearchResponse> call = apiService.searchAvailableTrips(TripType,departurePortId,returnPortId,departureDatePARSED,returnDatePARSED,pax);
call.enqueue(new Callback<TripSearchResponse>() {
#Override
public void onResponse(Call<TripSearchResponse> call, Response<TripSearchResponse> response) {
int statusCode = response.code();
switch(statusCode){
case 200:
default:
Snackbar.make(findViewById(android.R.id.content),"Error loading data. Network Error.", Snackbar.LENGTH_LONG).show();
break;
}
}
#Override
public void onFailure(Call<TripSearchResponse> call, Throwable t) {
Log.i(TAG, t.getMessage());
Snackbar.make(findViewById(android.R.id.content),"Error loading data. Network Error.", Snackbar.LENGTH_LONG).show();
}
});
}
The purpose is to make this callback function reusable so I can call it from several activities and get requested data as I need. What is the best way to implement this?
try this way, its dynamic way and easy to use:
Create Retforit Interface:
public interface ApiEndpointInterface {
#Headers("Content-Type: application/json")
#POST(Constants.SERVICE_SEARCH_TRIP)
Call<JsonObject> searchForTrip(#Body TripRequest objTripRequest);
}
Create Retrofit Class:
public class AppEndPoint {
private static Retrofit objRetrofit;
public static ApiEndpointInterface getClient() {
if (objRetrofit == null){
objRetrofit = new Retrofit.Builder()
.baseUrl(Constants.SERVER_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return objRetrofit.create(ApiEndpointInterface.class);
}
}
Create this helper Classes/Interfaces to hold web service callback:
public enum ResponseState {
SUCCESS,
FAILURE,
NO_CONNECTION
}
public enum RequestType {
SEARCH_FOR_TRIP // add name for each web service
}
public class Response {
public ResponseState state;
public boolean hasError;
public RequestType requestType;
public JsonObject result;
}
public interface RestRequestInterface {
void Response(Response response);
Context getContext();
}
public class ResponseHolder { used to hold the Json response could be changed as your response
#SerializedName("is_successful")
#Expose
private boolean isSuccessful;
#SerializedName("error_message")
#Expose
private String errorMessage;
public boolean isSuccessful() {
return isSuccessful;
}
public void setSuccessful(boolean successful) {
isSuccessful = successful;
}
public String getErrorMessage() {
return errorMessage;
}
public void setErrorMessage(String errorMessage) {
this.errorMessage = errorMessage;
}
}
public class AppClient {
private static ApiEndpointInterface objApiEndpointInterface;
private static Response objResponse;
private static Call<JsonObject> objCall;
// implement new method like below for each new web service
public static void searchForTrip(TripRequest objTripRequest, RestRequestInterface objRestRequestInterface) {
objResponse = new Response();
objResponse.state = ResponseState.FAILURE;
objResponse.hasError = true;
objResponse.requestType = RequestType.SEARCH_FOR_TRIP; // set type of the service from helper interface
objApiEndpointInterface = AppEndPoint.getClient();
objCall = objApiEndpointInterface.searchForTrip(objTripRequest);
handleCallBack(objRestRequestInterface);
}
private static void handleCallBack(final RestRequestInterface objRestRequestInterface) {
objCall.enqueue(new Callback<JsonObject>() {
#Override
public void onResponse(Call<JsonObject> call, retrofit2.Response<JsonObject> response) {
try {
ResponseHolder objResponseHolder = new Gson().fromJson(response.body(), ResponseHolder.class);
if (objResponseHolder.isSuccessful()) {
objResponse.state = ResponseState.SUCCESS;
objResponse.hasError = false;
objResponse.result = response.body();
} else {
objResponse.errorMessage = objResponseHolder.getErrorMessage();
}
objRestRequestInterface.Response(objResponse);
} catch (Exception objException) {
objResponse.errorMessage = objRestRequestInterface.getContext().getString(R.string.server_error);
objRestRequestInterface.Response(objResponse);
}
}
#Override
public void onFailure(Call<JsonObject> call, Throwable objThrowable) {
String errorMessage = "";
if (objThrowable instanceof IOException) {
errorMessage = objRestRequestInterface.getContext().getString(R.string.no_connection_error);
} else {
errorMessage = objRestRequestInterface.getContext().getString(R.string.server_error);
}
objResponse.errorMessage = errorMessage;
objRestRequestInterface.Response(objResponse);
}
});
}
}
then go to your activity of fragment and make the call like this:
public class MainActivity extends AppCompatActivity implements RestRequestInterface {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// initialize ids
// prepare to call web service
// 1.Initialize your object to be sent over web service
TripRequest objTripRequest = new TripRequest();
objTripRequest.id = 1;
// 2.Show loader
// 3.Make the call
AppClient.searchForTrip(objTripRequest, this);
}
#Override
public void Response(Response response) {
// hide loader
try {
if (response.state == ResponseState.SUCCESS && !response.hasError) {
// check the type of web service
if (response.requestType == RequestType.SEARCH_FOR_TRIP) {
// acces the return here from response.result
}
} else {
String errorMsg = response.hasError ? response.errorMessage : getString(R.string.no_connection_error);
// show the error to the user
}
} catch (Exception objException) {
// show the error to the user
}
}
#Override
public Context getContext() {
// do not forgit set the context here
// if fragment replace with getAcitvity();
return this;
}
}

Accepting multiple payloads from a Retrofit API request

My Retrofit API method is currently accepting one payload structure. However, the backend may return a different payload structure if there's any error in the request.
For example:
public void search(String term, final CallBack <ArrayList<String>> callBack) {
RetroGenerator.createService(APIServices.class).search(term).enqueue(new Callback<ArrayList<String>> () {
#Override
public void onResponse(Call<ArrayList<String>> call, Response<ArrayList<String>> response) {
if (response.isSuccessful()) {
callBack.onSuccess(response.body());
}
return;
}
callBack.onError();
}
#Override public void onFailure(Call<ArrayList<String>> call, Throwable t) {
callBack.onError();
}
});
}
The backend is returning an array of String values. However if an error occurs, backend may return the following payload structure:
{
"error": "Term can't be empty",
"code": 403
}
But the way my API method is setup, it only accepts one java model.
API Interface:
#FormUrlEncoded
#POST("api/v1/search.json")
Call<ArrayList<String>> search(#Field("term") String term);
Currently it's accepting only an ArrayList<String> and does not accept the custom error payload model. Given that I create a new model called Error:
public class Error {
public String error;
public int code;
}
How can I switch the retrofit API method's model when an error occurs?
You can have an ErrorUtils class to handle your unsuccessful responses:
public class ErrorUtils {
public static ApiError parseError(Response<?> response) {
Converter<ResponseBody, ApiError> converter = ServiceGenerator.retrofit().
responseBodyConverter(ApiError.class, new Annotation[0]);
ApiError apiError;
try {
apiError = converter.convert(response.errorBody());
} catch (IOException e) {
apiError = new ApiError();
}
return apiError;
}
}
Then when you find an unsuccessful response, just parse the response with the ErrorUtils class:
if (!response.isSuccessful()) {
// ...
ApiError apiError = ErrorUtils.parseError(response);
}
The ApiError class:
public class ApiError {
#SerializedName("error")
private String mErrorDescription;
#SerializedName("code")
private Integer mErrorCode;
public ApiError() {}
public void setErrorCode(Integer code) {
this.mErrorCode = code;
}
public Integer getErrorCode() {
return mErrorCode;
}
public String getErrorDescription() {
return mErrorDescription;
}
public void setErrorDescription(String errorDescription) {
mErrorDescription = errorDescription;
}
}

Wrong IllegalStateException in Retrofit JSON parsing

Pleace, help me to parse JSON. I always get an object:
IllegalStateException: Expected BEGIN_ARRAY but was BEGIN_OBJECT at
line 1 column 2 path $
I try to pars List<List<String>>, <List<String>> and <String> but get the same exeption.
Here is my JSON:
{"barcodes":[["1212"],["22222222222"],["22222321321"],["23565233665558488"],["2999300031242"],["6"]]}
Interface:
import java.util.List;
import retrofit2.Call;
import retrofit2.http.GET;
public interface RequestInterface {
#GET("barcodeinfo?getBarcodes")
Call<List<List<String>>> getBarcodeList();
}
obj:
public class SingleBarcode {
final String barcodes;
public SingleBarcode(String barcodes) {
this.barcodes = barcodes;
}
}
main:
void getRetrofitArray() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
RequestInterface service = retrofit.create(RequestInterface.class);
Call<List<List<String>>> call = service.getBarcodeList();
call.enqueue(new Callback<List<List<String>>>() {
#Override
public void onResponse(Call<List<List<String>>> call, Response<List<List<String>>> response) {
try {
List<List<String>> BarcodeData = response.body();
Log.d("MyLog", BarcodeData.size()+"");
} catch (Exception e) {
Log.d("MyLog", "There is an error");
e.printStackTrace();
}
}
#Override
public void onFailure(Call<List<List<String>>> call, Throwable t) {
Log.d("MyLog", "error " + t.toString());
}
});
}
use these POJO class
public class Result {
#SerializedName("barcodes")
#Expose
private List<List<String>> barcodes = new ArrayList<List<String>>();
/**
*
* #return
* The barcodes
*/
public List<List<String>> getBarcodes() {
return barcodes;
}
/**
*
* #param barcodes
* The barcodes
*/
public void setBarcodes(List<List<String>> barcodes) {
this.barcodes = barcodes;
}
}
use interface like this ...
#GET("barcodeinfo?getBarcodes")
Call<Result> getBarcodeList();
and call like this ....
Call<Result> call = service.getBarcodeList();
call.enqueue(new Callback<Result>() {
#Override
public void onResponse(Call<Result> call, Response<Result> response) {
Result r = response.body(); // you can initialize result r variable global if you have out side use of this response
}
#Override
public void onFailure(Call<Result> call, Throwable t) {
Log.d("MyLog", "error " + t.toString());
}
});
NOTE:- You are trying to access as LIST But in response it coming simple JSON OBJECT

Categories