REST POST HTTP JSON Objects 400 error Android - java

Since I got some bad reviews I am rewriting this question...
I have an HTTP REST server and a client (Android app). I have programmed several APIs that work just fine, however there is one that is giving me a 400 error, and if I put a breakpoint in the server, it does not even triggers it. So, I would like to understand why it fails :( ...
It is very simple, I have a value object called Alarm with a few attributes, that I want to POST to the server for registration of object in the database.
This is the output:
Callback failure for call to http://10.0.0.3:8080/...
java.io.IOException: Unexpected code Response{protocol=http/1.1, code=400, message=, url=http://10.0.0.3:8080/BiTrack_API/api/assets/registerAlarm}
at it.bitrack.fabio.bitrack.AlarmView$2$1.onResponse(AlarmView.java:438)
at okhttp3.RealCall$AsyncCall.execute(RealCall.java:135)
at okhttp3.internal.NamedRunnable.run(NamedRunnable.java:32)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
at java.lang.Thread.run(Thread.java:761)
This is my client side Android button listener:
View.OnClickListener addAlarmAction = new View.OnClickListener() {
#Override
public void onClick(View v) {
try {
alarm.setThreshold(Float.parseFloat(thresholdEditText.getText().toString()));
String alarmJson = j.makeJsonBodyForAlarmRegistration(alarm);
tagLinearLayout.setVisibility(view.GONE);
operatorLinearLayout.setVisibility(view.GONE);
thresholdLinearLayout.setVisibility(view.GONE);
assetSpinner.setSelection(0);
r.attemptAddNewAlarm(alarmJson,
new Callback() {
#Override public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
#Override public void onResponse(Call call, Response response) throws IOException {
try (final ResponseBody responseBody = response.body()) {
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
Headers responseHeaders = response.headers();
for (int i = 0, size = responseHeaders.size(); i < size; i++) {
System.out.println(responseHeaders.name(i) + ": " + responseHeaders.value(i));
}
final String responseBodyString = responseBody.string();
final int resultCode = response.code();
try {
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
Log.i("BiTrack", "attemptAddNewAlarm RESULT: " + resultCode);
executeAlarmRegistration(resultCode);
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
};
This is the code where I generate the Json body for the POST in the client:
public String makeJsonBodyForAlarmRegistration (Alarm alarm) {
Gson gson = new Gson();
String jsonAlarm = gson.toJson(alarm);
return jsonAlarm;
}
This is the actual POST code in the client (Android) side:
public void attemptAddNewAlarm(String json, Callback callback) throws Exception {
final OkHttpClient client = new OkHttpClient();
RequestBody body = RequestBody.create(JSON, json);
Request request = new Request.Builder()
.url(WEB_SERVER + "BiTrack_API/api/assets/registerAlarm")
.post(body)
.build();
client.newCall(request).enqueue(callback);
}
This is my server side code:
#POST
#Path("/registerAlarm")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public Response registerAlarm(Alarm alarm) {
System.out.println("Received API Call: registerAlarm for alarm tagId: " + alarm.getTagId() + " operatorId: " + alarm.getOperatorId() + " treshold: " + alarm.getThreshold());
DataProcessor dp = new DataProcessor();
AssetUpdateDAO aDAO = new AssetUpdateDAO();
ArrayList<Alarm> customerAlarms = aDAO.getUserAlarmsForAsset(alarm.getUserId(), alarm.getAssetId());
if (dp.isNewAlarmDuplicate(customerAlarms, alarm)) {
return Response.status(480).build(); // duplicated error
} else {
int response = aDAO.insertAssetUserAlarm(alarm.getUserId(), alarm.getAssetId(), alarm.getTagId(), alarm.getOperatorId(), alarm.getThreshold());
if (response == -5) {
return Response.status(484).build(); // something went wrong while inserting alarm into db
} else {
return Response.status(200).build();
}
}
}
This is my Alarm value object (identical class in client and server):
public class Alarm {
public Alarm() {
}
protected int id;
protected int userId;
protected int assetId;
protected int tagId;
protected int operatorId;
protected float threshold;
protected String networkAssetCode;
public String getNetworkAssetCode() {
return networkAssetCode;
}
public void setNetworkAssetCode(String networkAssetCode) {
this.networkAssetCode = networkAssetCode;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
public int getAssetId() {
return assetId;
}
public void setAssetId(int assetId) {
this.assetId = assetId;
}
public int getTagId() {
return tagId;
}
public void setTagId(int tagId) {
this.tagId = tagId;
}
public int getOperatorId() {
return operatorId;
}
public void setOperatorId(int operatorId) {
this.operatorId = operatorId;
}
public float getThreshold() {
return threshold;
}
public void setThreshold(float threshold) {
this.threshold = threshold;
}
}
I really appreciate any help...

In order to help you, the endpoint code is required. Now it is even unclear what technology stack is used for your API.
But from the information that is present... The endpoint considers your json as invalid.
400 Bad Request
The request could not be understood by the server due to malformed
syntax. The client SHOULD NOT repeat the request without
modifications.
In jax-rs the payload is first deserialized before it reaches the method that is bound to the url en http method.
Possibly the deserializing is failing and it never reaches the breakpoint you set.
What would be interesting is the following:
logs or exception from the server. The client exception is not that helpful, since the server returns this response.
the actual (json) payload that is send over the wire.
what deserialization mechanism is used at the server end? Reflection or did you make your own deserializer?

I found the issue! After 48 hours looking for the impossible, discovered that I had done a small update at the object attribute at the server side that had not been replicated in the client side...

Related

okhttp response.body().string() android.os.NetworkOnMainThreadExceptin [duplicate]

I want to use OkHttp library for networking in Android.
I started with the simple post example as written in their website:
public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
OkHttpClient client = new OkHttpClient();
String post(String url, String json) throws IOException {
RequestBody body = RequestBody.create(JSON, json);
Request request = new Request.Builder()
.url(url)
.post(body)
.build();
Response response = client.newCall(request).execute();
return response.body().string();
}
With this call:
String response = post("http://www.roundsapp.com/post", json);
This call ends with NetworkOnMainThreadException.
I could wrap the call with an AsyncTask, but as far as I understand from the examples, the OkHttp library should have already taken care of that..
Am I doing something wrong?
You should use OkHttp's async method.
public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
OkHttpClient client = new OkHttpClient();
Call post(String url, String json, Callback callback) {
RequestBody body = RequestBody.create(JSON, json);
Request request = new Request.Builder()
.url(url)
.post(body)
.build();
Call call = client.newCall(request);
call.enqueue(callback);
return call;
}
And then your response would be handled in the callback (OkHttp 2.x):
post("http://www.roundsapp.com/post", json, new Callback() {
#Override
public void onFailure(Request request, Throwable throwable) {
// Something went wrong
}
#Override public void onResponse(Response response) throws IOException {
if (response.isSuccessful()) {
String responseStr = response.body().string();
// Do what you want to do with the response.
} else {
// Request not successful
}
}
});
Or OkHttp 3.x/4.x:
post("http://www.roundsapp.com/post", "", new Callback() {
#Override
public void onFailure(Call call, IOException e) {
// Something went wrong
}
#Override
public void onResponse(Call call, Response response) throws IOException {
if (response.isSuccessful()) {
String responseStr = response.body().string();
// Do what you want to do with the response.
} else {
// Request not successful
}
}
});
Take a look at their recipes for more examples: http://square.github.io/okhttp/recipes/
According to the OkHttp docs:
It supports both synchronous blocking calls and async calls with callbacks.
Your example is on main thread and Android since version 3.0 throws that exception if you try to do network calls on main thread
Better option is to use it together with retrofit and Gson:
http://square.github.io/retrofit/
https://code.google.com/p/google-gson/
Here are the examples:
http://engineering.meetme.com/2014/03/best-practices-for-consuming-apis-on-android/
http://heriman.net/?p=5
If you follows these steps to implement OKHTTP, then definitely you'll call multiple API on multiple screen by applying only two lines of code
UpdateListener updateListener = new UpdateListener(HitAPIActivity.this, baseHTTPRequest);
updateListener.getJsonData();
Step 1:
baseHTTPRequest = new BaseHTTPRequest();
// baseHTTPRequest.setURL("https://api.geonames.org/citiesJSON?north=44.1&south=-9.9&east=-22.4&west=55.2&lang=de&username=demohttps://api.geonames.org/citiesJSON?north=44.1&south=-9.9&east=-22.4&west=55.2&lang=de&username=demo");
baseHTTPRequest.setURL("http://jsonparsing.parseapp.com/jsonData/moviesDemoItem.txt");
baseHTTPRequest.setRequestCode(reqType);
baseHTTPRequest.setCachedRequired(true);
UpdateListener updateListener = new UpdateListener(HitAPIActivity.this, baseHTTPRequest);
updateListener.executeRequest();
Step 2 : Create a request class
/**
* Created by Deepak Sharma on 4/7/16.
* This is a HTTP request class which has the basic parameters.
* If you wants to add some more parameters, please make a subclass of that class
* and add with your subclass. Don't modify this class.
*/
public class BaseHTTPRequest<T> {
private Context context;
private String URL;
private int requestCode;
private List<T> listParameters;
private String header;
private boolean isCachedRequired;
public Context getContext() {
return context;
}
public void setContext(Context context) {
this.context = context;
}
public void setURL(String URL) {
this.URL = URL;
}
public String getURL() {
return URL;
}
public int getRequestCode() {
return requestCode;
}
public void setRequestCode(int requestCode) {
this.requestCode = requestCode;
}
public List<T> getListParameters() {
return listParameters;
}
public void setListParameters(List<T> listParameters) {
this.listParameters = listParameters;
}
public String getHeader() {
return header;
}
public void setHeader(String header) {
this.header = header;
}
public boolean isCachedRequired() {
return isCachedRequired;
}
public void setCachedRequired(boolean cachedRequired) {
isCachedRequired = cachedRequired;
}
}
step 4 : Create a listener class
import android.util.Log;
import com.google.gson.Gson;
import java.io.IOException;
import dxswifi_direct.com.wifidirectcommunication.base.model.request.BaseHTTPRequest;
import okhttp3.Call;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Callback;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
/**
* Created by Deepak Sharma on 4/7/16.
* #email : dpsharma.sharma1#gmail.com
* This is a Simple java class which will help you for HTTP request/response and it will
* throw the response to your correspondance activity.
*/
public class UpdateListener {
private OnUpdateViewListener onUpdateViewListener;
OkHttpClient okHttpClient = new OkHttpClient();
BaseHTTPRequest mRequestModel;
private String mURL = null;
private Request mRequest = null;
public interface OnUpdateViewListener {
void updateView(String responseString, boolean isSuccess,int reqType);
}
public UpdateListener(OnUpdateViewListener onUpdateView, final BaseHTTPRequest requestModel) {
this.mRequestModel = requestModel;
this.onUpdateViewListener = onUpdateView;
if (requestModel.isCachedRequired())
{
/*File httpCacheDirectory = new File(requestModel.getContext().getCacheDir(), "responses");
Cache cache = null;
cache = new Cache(httpCacheDirectory, 10 * 1024 * 1024);
if (cache != null) {
okHttpClient.setCache(cache);
}*/
}
/*mURL = null;
if (requestModel.getListParameters()!=null && requestModel.getListParameters().size()>0)
{
HttpUrl.Builder urlBuilder = HttpUrl.parse(requestModel.getURL()).newBuilder();
List<RequestParameter> requestParameters = requestModel.getListParameters();
for (int i=0; i<requestParameters.size();i++)
{
urlBuilder.addQueryParameter(requestParameters.get(i).getKey(),requestParameters.get(i).getValue());
}
mURL = urlBuilder.build().toString();
}
else
{
mURL = requestModel.getURL();
}*/
mURL = requestModel.getURL();
if (mRequestModel.getListParameters()!=null && mRequestModel.getListParameters().size()>1)
{
MediaType JSON = MediaType.parse("application/json; charset=utf-8");
mRequest = new Request.Builder()
.url(mURL)
.post(RequestBody.create(JSON, new Gson().toJson(BaseHTTPRequest.class)))
.build();
}
else
{
mRequest = new Request.Builder()
.url(mURL)
.build();
}
}
public void executeRequest()
{
Call call = okHttpClient.newCall(mRequest);
call.enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
onUpdateViewListener.updateView(NetworkException.getErrorMessage(e), false, mRequestModel.getRequestCode());
}
#Override
public void onResponse(Call call, Response response) throws IOException {
if (!response.isSuccessful()) {
// You can also throw your own custom exception
throw new IOException("Unexpected code " + response);
} else {
Log.i("Response:",response.toString());
Log.i("Response body:",response.body().toString());
Log.i("Response message:",response.message());
onUpdateViewListener.updateView(response.body().string(),true, mRequestModel.getRequestCode());
}
// do something wih the result
}
});
}
}
step 5 : From the activity you requesting, implement listener
public class HitAPIActivity extends AppCompatActivity implements View.OnClickListener, UpdateListener.OnUpdateViewListener{
#Override
public void updateView(final String responseString, boolean isSuccess, int reqType) {
if (isSuccess)
{
if (!responseString.contains("failure")
&& !responseString.contains("Error")) {
// Handle request on the basis of Request Type.
switch (reqType) {
case ApiConstants.GET_CONTACTS:
break;
default:
break;
}
}
}
}

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

What am i doing wrong with my async call?

I have this bit of code:
public static void main(String[] args) throws UnirestException {
ArrayList<Stock> listStock
= getAllAvailableStocks("https://api.iextrading.com/1.0/ref-data/symbols");
//doing more actions after the one before, using the data from the listStock etc.
}
private static ArrayList<Stock> getAllAvailableStocks(String url) {
ArrayList<Stock> stocks = new ArrayList<Stock>();
Future<HttpResponse<JsonNode>> future = Unirest.get(url)
.header("accept", "application/json")
.asJsonAsync(new Callback<JsonNode>() {
public void failed(UnirestException e) {
System.out.println("The request has failed");
}
public void completed(HttpResponse<JsonNode> response) {
ObjectMapper objectMapper = new ObjectMapper();
try {
listStock = objectMapper.readValue(response.getRawBody(), new TypeReference<List<Stock>>(){});
} catch (Exception e) {
System.out.println("all is fucked");
}
return listStock;
}
public void cancelled() {
System.out.println("The request has been cancelled");
}
});
}
I am a newbie in java, i want to do the following:
1) I want to do async call to get and extract a list of stocks, only after the request completed i want to do the next things in the main method.
2) How do i extract data from the method i built so i can use the data outside of the method?
3) If i need to do the following:
getAllAvailableStocks("https://api.iextrading.com/1.0/ref-data/symbols",new Callback<JsonNode>() {
public void failed(UnirestException e) {
System.out.println("The request has failed");
}
public void completed(HttpResponse<JsonNode> response) {
ArrayList<Stock> listStock = new ArrayList<Stock>();
ObjectMapper objectMapper = new ObjectMapper();
int code = response.getStatus();
System.out.println(code);
try {
listStock = objectMapper.readValue(response.getRawBody(), new TypeReference<List<Stock>>(){});
} catch (Exception e) {
}
System.out.println(listStock);
}
public void cancelled() {
System.out.println("The request has been cancelled");
}
});
}
private static Future<HttpResponse<JsonNode>> getAllAvailableStocks(String url,Callback<JsonNode> cb) {
return Unirest.get(url)
.header("accept", "application/json")
.asJsonAsync(cb);
}
Or something of that sort, it makes the code horrible, and when i want to do much more async requests after, i have a callback hell here, is there any way to avoid it? what are my options here?
I think your are mixing up asnychronous and synchronous.
If you
want to do async call to get and extract a list of stocks, only after the request completed I want to do the next things in the main method
then you actually want to perform a synchronous call.
An asynchronous call would be to perform the request, then doing other things (not related to the request) and at some point in the future you get the result of the request and handle it.
To perform a synchronous call, which is probably what you want, try to adapt your code like this:
private static ArrayList<Stock> getAllAvailableStocks(String url) {
ArrayList<Stock> stocks = new ArrayList<Stock>();
Future<HttpResponse<JsonNode>> future = Unirest.get(url)
.header("accept", "application/json")
.asJsonAsync(new Callback<JsonNode>() {
public void failed(UnirestException e) {
System.out.println("The request has failed");
}
public void completed(HttpResponse<JsonNode> response) {
System.out.println("The request succeeded");
}
public void cancelled() {
System.out.println("The request has been cancelled");
}
});
HttpResponse<JsonNode> response = future.get(); // NOTE: This call is blocking until the request is finished
if (response != null && response.getStatus() == 200) {
JsonNode body = response.getBody();
// TODO Parse body and add items to `stocks`
}
return stocks;
}
This method can be used like this:
ArrayList<Stock> stocks = getAllAvailableStocks(...);
stocks.forEach(x -> System.out.println(x));
Edit
If you want to handle the result asynchronously without providing callbacks, you could use a CompletableFuture. Consider the following snippet as a starting point which does not handle unsuccessful calls.
private static CompletableFuture<ArrayList<Stock>> getAllAvailableStocks(String url) {
CompletableFuture<ArrayList<Stock>> result = new CompletableFuture<>();
Future<HttpResponse<JsonNode>> future = Unirest.get(url)
.header("accept", "application/json")
.asJsonAsync(new Callback<JsonNode>() {
public void failed(UnirestException e) {
System.out.println("The request has failed");
}
public void completed(HttpResponse<JsonNode> response) {
System.out.println("The request succeeded");
ArrayList<Stock> stocks = new ArrayList<Stock>();
if (response != null && response.getStatus() == 200) {
JsonNode body = response.getBody();
// TODO Parse body and add items to `stocks`
}
result.complete(stocks);
}
public void cancelled() {
System.out.println("The request has been cancelled");
}
});
return result;
}
The method can be used as follows:
CompletableFuture<ArrayList<Stock>> stocksFuture = getAllAvailableStocks(...);
stocksFuture.thenAccept((stocks) -> {
// NOTE: This will be called after and only if the request succeeded
stocks.forEach(x -> System.out.println(x));
});
System.out.println("This is probably executed before the above request finished.");
Thread.sleep(10000); // If you are calling from your `main` method: Prevent JVM exit

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

How to design a class that can pass data from server when data is finish downloading

I'm new on Android development and I'm learning how to use MVP pattern correctly in recently.
Now I'm facing a tricky problem, hope can get some helpful suggestion or solution from here.
First, here is my presenter
public class MVPPresenter {
private MVPView mvpView;
public MVPPresenter(MVPView mvpView) {
this.mvpView = mvpView;
}
public void loadData() {
mvpView.startLoading();
final List<MVPModel> list = new ArrayList<>();
//the part that I trying to extract starts here.
Call call = DataRetriever.getDataByGet(URLCombiner.GET_FRONT_PAGE_ITEMS);
call.enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
mvpView.errorLoading();
}
#Override
public void onResponse(Call call, Response response) throws IOException {
if (response.isSuccessful()) {
try {
JSONObject result = new JSONObject(response.body().string());
int errorCode = result.getInt("ErrorCode");
if (errorCode == 0) {
JSONArray value = result.getJSONObject("Value").getJSONArray("hot");
for (int i = 0; i < value.length(); i++) {
MVPModel mvpModel = new MVPModel();
String name = null;
String image = null;
try {
name = value.getJSONObject(i).getString("title");
image = URLCombiner.IP + value.getJSONObject(i).getString("pic");
} catch (JSONException e) {
e.printStackTrace();
}
mvpModel.setName(name);
mvpModel.setImage(image);
list.add(mvpModel);
}
if (list.size() > 0) {
mvpView.successLoading(list);
mvpView.finishLoading();
} else {
mvpView.errorLoading();
}
} else {
mvpView.errorLoading();
}
} catch (JSONException e) {
e.printStackTrace();
}
} else {
mvpView.errorLoading();
}
}
});
//the part that I trying to extract ends here.
}
}
As you can see, I'm trying to extract the part which is using OKHttp libs into a class (I call it data manager) which I hope it can handle every task between server and client. But here's the thing, when I trying to pass the result from the data manager to presenter, I got NullPointException because of the async mechanism.
I would like to know how to passing the data, which is come from server in async, to the presenter when the data has finish downloading.
And here's my ideal data manager, I know this might looks stupid but I think this can make my problem more clearly.
public class LoadServerData {
private static JSONArray arrayData = new JSONArray();
public static JSONArray getServerData() {
Call call = DataRetriever.getDataByGet(URLCombiner.GET_FRONT_PAGE_ITEMS);
call.enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
}
#Override
public void onResponse(Call call, Response response) throws IOException {
if (response.isSuccessful()) {
try {
JSONObject result = new JSONObject(response.body().string());
int errorCode = result.getInt("ErrorCode");
if (errorCode == 0) {
arrayData = result.getJSONObject("Value").getJSONArray("hot"); //the data I would like to return.
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}
});
return arrayData; //this is gonna be an empty data.
}
}
I've reading some article that might can solve my problem, but still not getting any fine answer. Perhaps I've using wrong keyword I think. Hopes you guys can give me some ideas or solutions to help me or inspire me.
P.S. version of OKhttp libs is 3.7.0
Create a simple Listener so it can be called whenever the server call finishes:
public class LoadServerData {
public static interface Listener {
public void onSuccess(JSONArray data);
public void onError(Exception error);
}
public static void getServerData(Listener listener) {
Call call = DataRetriever.getDataByGet(URLCombiner.GET_FRONT_PAGE_ITEMS);
call.enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
listener.onError(e);
}
#Override
public void onResponse(Call call, Response response) throws IOException {
if (response.isSuccessful()) {
try {
JSONObject result = new JSONObject(response.body().string());
int errorCode = result.getInt("ErrorCode");
if (errorCode == 0) {
JSONArray arrayData = result.getJSONObject("Value").getJSONArray("hot"); //the data I would like to return.
listener.onSuccess(arrayData);
} else {
listener.onError(...);
}
} catch (JSONException e) {
e.printStackTrace();
listener.onError(e);
}
} else {
listener.onError(...);
}
}
});
}
}

Categories