Retrofit response body sometimes error, but sometimes success - android

I'm getting some error when post some data.
My app could register a new user, send data register to server and then return a result.
result if register is success
{
"res": "success"
}
result if register is failed
{
"res": "customer id not found"
}
Here my piece of code for post data
Register register = new Register(nameStr, emailStr, phoneStr, passStr, rePassStr,customerIDStr );
Call<ResultRegister> call = apiInterface.registration(register);
call.enqueue(new Callback<ResultRegister>() {
#Override
public void onResponse(Call<ResultRegister> call, Response<ResultRegister> response) {
String responseString;
responseString = response.body().getRes();
if(responseString.equalsIgnoreCase("success")){
Toast.makeText(getActivity(), "Register Success", Toast.LENGTH_SHORT).show();
Fragment fragment = new LoginFragment();
FragmentManager fragmentManager = getFragmentManager();
fragmentManager.beginTransaction().replace(R.id.wrap_login_container, fragment)
.addToBackStack(null).commit();
}else{
Toast.makeText(getActivity(), "Register Failed", Toast.LENGTH_SHORT).show();
}
}
#Override
public void onFailure(Call<ResultRegister> call, Throwable t) {
t.printStackTrace();
call.cancel();
Toast.makeText(getActivity(), "Please check your network connection and internet permission", Toast.LENGTH_LONG).show();
}
});
Everything is fine if the register return success, but if register return error a null pointer exception appears, pointing to this line:
responseString = response.body().getRes();
EDIT
this is my ResultRegister class
public class ResultRegister {
#SerializedName("res")
String res;
public String getRes() {
return res;
}
public void setRes(String res) {
this.res = res;
}
}
Any solution??

I have solved my problem. The main problem is when the register is failed, the error code is 500 (internal error server). So the response.body() is null.

Related

When making API call LiveData returns old value first and then the new one

I am using LiveData and RoomDB and my problem is that when I put the observe() on my LiveData and then make the request to fetch updates from the server, it first gives me the previous value that it had requested before and then once the request is done it gives me the new value that it got from the server.
When no previous request have been made it works like I want it to, meaning it will fetch the data straight from the server and then show it, but when a request was previously made and the LiveData already had a value then it will first show the old one and then the new one.
What I want is for it to show only the new one.
The variables (viewModel, repository, etc) are coming from dagger.
Main Activity:
viewModel.getPointGeoLocation(changedyPoints.get(0)).observe(this, new Observer<LatLng>() {
#Override
public void onChanged(#Nullable LatLng latLng) {
if (latLng != null) {...} else {...}}});
ViewModel Class:
public LiveData<LatLng> getPointGeoLocation(Point Point) {
repository.fetchGeoLocationCoordinates(Point);
return repository.getPointGeoLocationNetworkSource();
}
RepositoryImpl Class:
#Override
public void fetchGeoLocationCoordinates(Point Point) {
pointNetworkDataSource.fetchGeoLocations(point);
}
#Override
public LiveData<LatLng> getPointGeoLocationNetworkSource() {
return pointNetworkDataSource.getPointGeoLocationData();
}
NetworkDataSource Class:
public void fetchGeoLocations(Point point) {
executors().getNetworkIO().execute(new Runnable() {
#Override
public void run() {
AuthTokenUtil.waitForAuthToken(authTokenRepository);
AuthToken authToken = authTokenRepository.getAuthToken().getValue();
if (authToken.isValid()) {
pointBackendApi.getGeoLocations(authToken.getToken(), point,
new PointBackendApi.GeoLocationHandler() {
#Override
public void update(LatLng latLng) {
pointGeoLocationData.postValue(latLng);
}
});
} else {
Log.d(TAG, "invalid auth token");
}
}
});
}
public LiveData<LatLng> getPointGeoLocationData() {
return pointGeoLocationData;
}
And Finally the Backend API:
#Override
public void getGeoLocations(String token, Point point, GeoLocationHandler geoLocationHandler) {
http.getJson(POINT_GET_GEO_LOCATION_UPDATE, token, point.domesticAddress.formattedString, new Callback() {
#Override
public void onResponse(#NonNull Call call, #NonNull Response response) {
try (ResponseBody body = response.body()) {
if (body == null || body.contentLength() == 0) {
Log.d(TAG, "received no response");
return;
}
String res = body.string();
LatLng result = new Gson().fromJson(res, LatLng.class);
if (result != null && (result.latitude != 0 && result.longitude != 0)) {
Log.i(TAG, "received" + " points" + result.toString());
geoLocationHandler.update(result);
} else {
geoLocationHandler.update(null);
Log.d(TAG, "Received 0, 0 coordinates");
}
} catch (Exception ex) {
Log.e(TAG, ex.getMessage() != null ? ex.getMessage() : "HTTP problem");
}
}
#Override
public void onFailure(#NonNull Call call, #NonNull IOException e) {
Log.w(TAG, "get failed");
}
});
}
So basically If I have had a response with value null before and then call this API again it will first return null and then return the correct response.
The first method viewModel.getPointGeoLocation() is called multiple times so that might be the issue but for me it seems that it needs to be inside of the method and act when the button is clicked because the method it is in contains a lot of data that the onChanged() should use.

Retrofit returns 200 and starts next activity even when input is wrong

I am using retrofit to authenticate user but when wrong input is entered, response code is 200 and the next activity starts.
public void serverLogin(){
// Create handle for the RetrofitInstance interface
apiInterface = RetrofitInstance.getRetrofitInstance().create(APIInterface.class);
UserModel userModel = new UserModel();
userModel.setUsername(username);
userModel.setPassword(password);
Call<IDModel> activate = apiInterface.userLogin(userModel);
activate.enqueue(new Callback<IDModel>() {
#Override
public void onResponse(Call<IDModel> call, Response<IDModel> response) {
if (response.isSuccessful()) {
int statusCode = response.code();
response.body();
customerID = response.body().getCustomer_id();
subID = response.body().getSub_id();
idDB.addCustomerID(customerID, subID);
session.setLoggedIn(true);
Intent intent = new Intent(Login.this, Drawer.class);
intent.putExtra("cusID", customerID);
startActivity(intent);
Toast.makeText(Login.this, "SUCCESS", Toast.LENGTH_SHORT).show();
Log.d("onresponse", "" + statusCode);
}
else {
Toast.makeText(Login.this, "Login failed", Toast.LENGTH_SHORT).show();
}
}
#Override
public void onFailure(Call<IDModel> call, Throwable t) {
Toast.makeText(Login.this, "" + t.getMessage(), Toast.LENGTH_SHORT).show();
Log.d("onfailure"," " + t . getMessage());
}
});
}
The else part always gets skipped. When it is not successful, I want to notify the user and not start next activity.

how to pass header in retrofit 1.9 API

Requirement->
To get list of Objects Using GET Request:
passing Request headers:
X-ACCESS-TOKEN = Token received after successful sign in
X-USER-EMAIL = Email used in login.
I am using this code to login->
private void normalLoginToServer() {
final ProgressDialog progressDialog = GeneralUtil.createProgressDialog(this, "Logging into app..");
progressDialog.show();
Instead of using JSONObject, i need to pass Request Headers.
in a below Astrike code. how to pass Headers.? please help me.
***JSONObject outer_body = new JSONObject();
JSONObject body = new JSONObject();
try {
body.put(Constants.USER_EMAIL, _emailText.getText().toString().trim());
body.put(Constants.USER_PWD, _passwordText.getText().toString().trim());
outer_body.put(Constants.USER, body);***
try {
TypedInput typedInput = new TypedByteArray("text/plain", outer_body.toString().getBytes("UTF-8"));
apiService.loginToServer(typedInput, new Callback<UserInfo>() {
#Override
public void success(UserInfo response, Response response2) {
int status = response2.getStatus();
switch (status) {
case 200:
if (progressDialog.isShowing())
progressDialog.dismiss();
if (response == null)
return;
String status1 = response.getStatus();
if (status1.equalsIgnoreCase("success")) {
Toast.makeText(context, "Successful login", Toast.LENGTH_SHORT).show();
Data data = response.getData();
User user = data.getUser();
String token = user.getAccess_token();
Intent intent = new Intent(context, MainActivity.class);
intent.putExtra(Constants.ACCESS_TOKEN, token);
startActivity(intent);
finish();
} else if (status1.contains("failure")) {
Toast.makeText(context, "Username not found", Toast.LENGTH_SHORT).show();
}
break;
case 500:
Toast.makeText(context, getResources().getString(R.string.server_error), Toast.LENGTH_SHORT).show();
break;
}
}
#Override
public void failure(RetrofitError retrofitError) {
if (progressDialog.isShowing())
progressDialog.dismiss();
if (retrofitError != null) {
if (retrofitError.getKind() != null) {
if (retrofitError.getKind().equals(RetrofitError.Kind.NETWORK)) {
Toast.makeText(context, "Check your network connection and try again later",
Toast.LENGTH_SHORT).show();
}
} else if (retrofitError.getResponse() != null) {
if (retrofitError.getResponse().getStatus() == 500) {
Toast.makeText(context, getResources().getString(R.string.server_error), Toast.LENGTH_SHORT).show();
}
}
}
}
});
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
} catch (JSONException e) {
e.printStackTrace();
}
}
Thanks in Advenced.!!
You can use #Header in your retrofit api interface. For example, something like:
void loginToServer(#Header("your_header") String yourHeaderValue, Callback<UserInfo> callback);
Assuming you want to pass static headers(those who won't change for a individual requests).
for example this code adds cache control header to /tasks request
public interface UserService {
#Headers("Cache-Control: max-age=640000")
#GET("/tasks")
List<Task> getTasks();
}
and if you need to pass dynamic headers (those changing on individual requests)
public interface UserService {
#GET("/tasks")
List<Task> getTasks(#Header("Content-Range") String contentRange);
}
More on Retrofit Add Custom Request Header

Retrofit Add custom object in Error callback. onFailure

hi i am using retrofit my callback is as follow
#Override
public void onResponse(final Call<T> call, Response<T> response) {
if (response.isSuccessful()) {
passing this to my view
} else {
// as this failed other then 200 retroCallback.onFailure(call, new Throwable(""));
}
}
#Override
public void onFailure(Call<T> call, Throwable t) {
retroCallback.onFailure(call, t);
}
so in this how i can pass my ErrorBean instead of Throwable anyway we can pass custom model in onFailure ? as my server giving me response in some formate i want to pass that format .. i am using retrofit 2.1.0
You can subclass Throwable and pass additional object using composition.
public class ErrorBean extends Throwable {
public ErrorPayload payload = null;
public ErrorBean(ErrorPayload payload) {
this.payload = payload;
}
}
Then, in onError:
#Override
public void onFailure(Call<T> call, Throwable t) {
retroCallback.onFailure(call, t);
if (t instanceof ErrorBean) {
// do your stuff here
((ErrorBean)t).payload.text;
}
}
AFAIK,, Retrofit's onFailure is used for handling errors like no internet connection.
To handle the error response from your Server, Error response, I mean response from Server with 4xx status code but with some JSON response for client to handle it.
Say, you are getting this error structure from Server:
{
statusCode: 409,
message: "Email address already registered"
}
This error will be captured in onResponse(...). To handle this, create your
public class ErrorBean {
private int statusCode;
private String message;
public ErrorBean() {
}
public int status() {
return statusCode;
}
public String message() {
return message;
}
}
Create a simple ErrorHandler util:
public class ErrorUtils {
public static ErrorBean parseError(Response<?> response) {
Converter<ResponseBody, ErrorBean> converter =
ServiceGenerator.retrofit()
.responseBodyConverter(ErrorBean.class, new Annotation[0]);
ErrorBean error;
try {
error = converter.convert(response.errorBody());
} catch (IOException e) {
return new ErrorBean();
}
return error;
}
}
And finally,
...
call.enqueue(new Callback<SuccessResponse>() {
#Override
public void onResponse(Call<SuccessResponse> call, Response<SuccessResponse> response) {
if (response.isSuccessful()) {
// use response data and do some fancy stuff :)
} else {
// parse the response body …
ErrorBean error = ErrorUtils.parseError(response);
// … and use it to show error information
// … or just log the issue like we’re doing :)
Log.d("error message", error.message());
}
}
#Override
public void onFailure(Call<User> call, Throwable t) {
// there is more than just a failing request (like: no internet connection)
}
});
Hope you got the point..!!!

Async-http often hangs.

I start a ProgressDialog when I start my async request and on seemingly random occasions the dialogue does not dismiss (code does not fire onSuccess or onFailure). I handle both possible success responses from the server (one of which is an error) and I have a failure block so in theory the ProgressDialog should always dismiss. Can someone tell what event I am missing? Or is there a better structure?
My code structure:
I have a Gateway class that handles all the networking
The calling calls handles the .show() and .dismiss() events for the dialog
Gateway:
public static void post(String url, RequestParams params, AsyncHttpResponseHandler responseHandler) {
client.post(getAbsoluteUrl(url), params, responseHandler);
}
public static void loadItems(final ItemAdapter itemAdapter, int itemID) {
final String url = String.format(Constants.URL_ITEMS, itemID);
post(url, null, new JsonHttpResponseHandler() {
#Override
public void onSuccess(JSONObject response) {
try {
if (!response.isNull("items")) {
itemAdapter.updateData(items);
} else if (!response.isNull("error")) {
itemAdapter.signalError(response.getString("error"));
}
} catch (JSONException e) {
itemAdapter.signalError("An unknown error has occurred");
e.printStackTrace();
}
}
#Override
public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error)
{
if (error instanceof SocketTimeoutException || error instanceof ConnectTimeoutException) {
itemAdapter.signalError("Connection timeout! Please check that you are connected to the internet");
} else {
itemAdapter.signalError("An unknown error has occurred");
}
}
});
}
The adapter:
public ItemAdapter(Context context, int itemID) {
progressDialog = ProgressDialog.show(context, "Items",
"Loading items", true);
Gateway.loadItems(this, itemID);
}
public void updateData(ArrayList<Items> items) {
progressDialog.dismiss();
this.items = items;
notifyDataSetChanged();
}
public void signalError(String errorMessage) {
progressDialog.dismiss();
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle("Error")
.setMessage(errorMessage)
.setNegativeButton("OK", null).show();
}
I have no idea what your code is doing, but your if statements look suspicious.
if (!response.isNull("items")) {
itemAdapter.updateData(items);
} else if (!response.isNull("error")) {
itemAdapter.signalError(response.getString("error"));
}
could it be that none of the two conditions are met? If so, the dialog will not be dismissed. You only dismiss it on exception or if one of the above conditions are met.
I read somewhere that there is a design flaw in the library and people had some problems with the same issue.
But the most reliable solution was to override all the onSuccess and onFailure methods in the JsonHttpResponseHandler. In that way you can be sure that a communication is going on.
Hope this helps
Handle else case also onSuccess method like
#Override
public void onSuccess(JSONObject response) {
try {
if (!response.isNull("items")) {
itemAdapter.updateData(items);
} else if (!response.isNull("error")) {
itemAdapter.signalError(response.getString("error"));
} else {
// Code to dismiss the dialog
}
} catch (JSONException e) {
itemAdapter.signalError("An unknown error has occurred");
e.printStackTrace();
}
}
If your main concern is just closing ProgressDialog .. then try like this .
try{
runOnUiThread(new Runnable() {
public void run() {
progressDialog.dismiss();
}
});
}catch(Exception beauty)
{
// Log error if any ..
}
Hope it helps!

Categories

Resources