I have an app that posts several variables to an API and returns responses from the API via Retrofit2. This is actually a payment model where the user's paymentAmount , paymentType ,userID ,billNumber is sent to the API for payment process and responses are received in an AlertDialog to tell whether payment process was successful or not.
The issue here is that the API successfully receives my variables to initialize a payment process however it doesn't return the onResponse method. Rather the onFailure method is called making it unable to receive responses from the API.
This is the log i recieve when i call Throwable t.getCause() from the onFailure method. Please Help.
04-23 20:20:32.368 25592-25592/com.example.gamor.rhema E/ContentValues: Unable to submit post to API.java.net.SocketException: Socket closed
API Response Format
{
"RespCode": "0",
"PaymentNumber": "PYMNT000000000375",
"PaymentAmount": 0.5,
"PaymentDate": "4/23/2018 6:55:09 PM",
"Respmessage": "Successful"
}
My API Interface - APIServicePayBill.java
public interface APIServicePayBill {
#POST("PayBills")
#FormUrlEncoded
Call<PostPayBill> savePostPayBill(#Field("AccountNumber") String AccountNumber,
#Field("Amount") float Amount,
#Field("Paytype") String Paytype,
#Field("BillNumber") String BillNumber);
}
JSON POJO Class - PostPayBill.java
public class PostPayBill {
#SerializedName("RespCode")
#Expose
private String respCode;
#SerializedName("PaymentNumber")
#Expose
private String paymentNumber;
#SerializedName("PaymentAmount")
#Expose
private float paymentAmount;
#SerializedName("PaymentDate")
#Expose
private String paymentDate;
#SerializedName("Respmessage")
#Expose
private String respmessage;
public String getRespCode() {
return respCode;
}
public void setRespCode(String respCode) {
this.respCode = respCode;
}
public String getPaymentNumber() {
return paymentNumber;
}
public void setPaymentNumber(String paymentNumber) {
this.paymentNumber = paymentNumber;
}
public float getPaymentAmount() {
return paymentAmount;
}
public void setPaymentAmount(float paymentAmount) {
this.paymentAmount = paymentAmount;
}
public String getPaymentDate() {
return paymentDate;
}
public void setPaymentDate(String paymentDate) {
this.paymentDate = paymentDate;
}
public String getRespmessage() {
return respmessage;
}
public void setRespmessage(String respmessage) {
this.respmessage = respmessage;
}
#Override
public String toString() {
return "PostPayBill{" +
"respCode='" + respCode + '\'' +
", paymentNumber='" + paymentNumber + '\'' +
", paymentAmount=" + paymentAmount +
", paymentDate='" + paymentDate + '\'' +
", respmessage='" + respmessage + '\'' +
'}';
}
}
ApiUtils Class - ApiUtils.java
public class ApiUtils {
private ApiUtils() {}
public static final String BASE_URL = "http://10.1.123.11/api/";
public static APIServicePayBill getUserServicePayBill(){
return RetrofitClient.getClient(BASE_URL).create(APIServicePayBill.class);
}
}
API Initialising
apiServicePayBill = ApiUtils.getUserServicePayBill();
Payment Method
private void sentPayBillsDetails(String AccountNumber, float Amount, String Paytype, String BillNumber ) {
apiServicePayBill.savePostPayBill(AccountNumber,Amount,Paytype,BillNumber).enqueue(new Callback<PostPayBill>() {
#Override
public void onResponse(Call<PostPayBill> call, Response<PostPayBill> response) {
if (response.isSuccessful()){
Log.i(TAG, "post submitted to API." + response.body().toString());
if ( progressDialog!=null && progressDialog.isShowing() ){
progressDialog.dismiss();
}
AlertDialog.Builder registrationAlert = new AlertDialog.Builder(MakePaymentActivity.this);
registrationAlert.setTitle(Html.fromHtml("<font color='#125688'>PAYMENT STATUS</font>"));
registrationAlert.setIcon(R.drawable.ic_check_box);
registrationAlert.setMessage("Payment Number: "+response.body().getPaymentNumber()+
"\nPayment Amount: "+response.body().getPaymentAmount()+
"\nPayment Date: "+response.body().getPaymentDate()+
"\nMessage: "+response.body().getRespmessage());
registrationAlert.setPositiveButton("OK", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
gotoRegistrationPage();
}
});
registrationAlert.setCancelable(false);
AlertDialog alertDialog = registrationAlert.create();
alertDialog.show();
}
}
#Override
public void onFailure(Call<PostPayBill> call, Throwable t) {
Log.e(TAG, "Unable to submit post to API."+t.getCause());
if ( progressDialog!=null && progressDialog.isShowing() ){
progressDialog.dismiss();
}
AlertDialog.Builder registrationAlert = new AlertDialog.Builder(MakePaymentActivity.this);
registrationAlert.setTitle(Html.fromHtml("<font color='#125688'>PAYMENT STATUS</font>"));
registrationAlert.setIcon(R.drawable.ic_check_box);
registrationAlert.setMessage("Unable to submit post to API");
registrationAlert.setPositiveButton("OK", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
gotoFaliedPage();
}
});
registrationAlert.setCancelable(false);
AlertDialog alertDialog = registrationAlert.create();
alertDialog.show();
}
});
}
Gradle File
// Retrofit
implementation 'com.squareup.retrofit2:retrofit:2.4.0'
// JSON Parsing
implementation 'com.google.code.gson:gson:2.6.1'
implementation 'com.squareup.retrofit2:converter-gson:2.4.0'
Related
I have my own rest api for application so when I create post request to api I want to wait for the response and then do something. how I can wait for the call response.
insert TicketList
public long insertApi(TicketListTable ticketListTable) throws IOException {
Call<TicketListTable> ticketListTableCall = dataService.createTicketList(ticketListTable);
ticketListTableCall.enqueue(new Callback<TicketListTable>() {
#Override
public void onResponse(Call<TicketListTable> call, Response<TicketListTable> response) {
}
#Override
public void onFailure(Call<TicketListTable> call, Throwable t) {
}
});
// want to wait for the call to complete then return the value
//ticketListTableCall.execute();
Log.d("CODE", "insertApi: "+ ticketListTableCall.isExecuted());
return repo.insert(ticketListTable);
}
this method is called when button is clicked
public void saveListToDb(String[] split, String regexData){
String newListName = listName.getText().toString();
SimpleDateFormat sd = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
TicketListTable newTicketListTable = new TicketListTable();
newTicketListTable.setTicketListName(newListName);
newTicketListTable.setTicketListCreated(sd.format(Calendar.getInstance().getTime()));
newTicketListTable.setTicketListUpdated(sd.format(Calendar.getInstance().getTime()));
long ticketTableListId = 0;
try {
ticketTableListId = ticketTableVm.insert(newTicketListTable);
} catch (IOException e) {
e.printStackTrace();
}
if(ticketTableListId<=0){
throw new ArithmeticException();
}
for (String s : split) {
String[] values = s.split(regexData);
String number = values[0].replace("\n", "").replace("\r", "");
String info = values[1];
String warningNote = values[2];
String warning = values[3];
String customer = values[4];
int useable = Integer.parseInt(values[5].replaceAll("\\s+", "").replace("\n", "").replace("\r", ""));
TicketTable ticketTable = new TicketTable(number, customer, info, warningNote, useable, ticketTableListId,warning);
ticketTableVm.insert(ticketTable);
}
}
insert Ticket api call, before calling this I want to verify that the previous call has been successfully executed
public void insert(TicketTable ticketTable) throws IOException {
Call<TicketTable> ticketCall = dataService.createTicket(ticketTable);
//Response resp = ticketCall.execute();
ticketCall.enqueue(new Callback<TicketTable>() {
#Override
public void onResponse(Call<TicketTable> call, Response<TicketTable> response) {
repo.insert(ticketTable);
}
#Override
public void onFailure(Call<TicketTable> call, Throwable t) {
}
});
I'm trying to use RETROFIT to get an inform about COVID-19 data. And I want to show the latest data, so I tried
content1 = covid_post_data.getPositive() + "\n";
content2 = covid_post_data.getDeath() + "\n";
content3 = " (+" + covid_post_data.getPositiveIncrease() + ")\n";
content4 = " (+" + covid_post_data.getDeathIncrease() + ")\n";
content5 = " Updated : " + covid_post_data.getDate() + "\n";
textViewResult.setText(content1);
textViewResult2.setText(content2);
textViewResult3.setText(content3);
textViewResult4.setText(content4);
textViewResult5.setText(content5);
But it is not working.it didn't show any data. how can i show the latest data in JSON file with using retrofit? below is my whole code of main activity
1.MainActivity
public class COVIDActivity extends AppCompatActivity {
private TextView textViewResult;
private TextView textViewResult2;
private TextView textViewResult3;
private TextView textViewResult4;
private TextView textViewResult5;
private static final String TAG = "COVIDActivity";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.covid_menu);
//How go_back_button works
ImageButton bo_back_Button = (ImageButton) findViewById(R.id.covid_to_main);
bo_back_Button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent move_main_covid = new Intent(COVIDActivity.this,MainActivity.class);
startActivity(move_main_covid);
}
});
textViewResult = findViewById(R.id.text_view_result_positive);
textViewResult2 = findViewById(R.id.text_view_result_death);
textViewResult3 = findViewById(R.id.text_view_result_positive_increase);
textViewResult4 = findViewById(R.id.text_view_result_death_increase);
textViewResult5 = findViewById(R.id.today_date);
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://covidtracking.com/api/v1/")
.addConverterFactory(GsonConverterFactory.create())
.build();
JsonPlaceHolderApi jsonPlaceHolderApi = retrofit.create(JsonPlaceHolderApi.class);
Call<List<COVID_Post_Data>> call = jsonPlaceHolderApi.get_covid_post();
call.enqueue(new Callback<List<COVID_Post_Data>>() {
#Override
public void onResponse(Call<List<COVID_Post_Data>> call, Response<List<COVID_Post_Data>> response) {
if (!response.isSuccessful()) {
textViewResult.setText(("Code: " + response.code()));
return;
}
List<COVID_Post_Data> posts = response.body();
//Give message if fail, TAG is COVIDActivity so that it will show log in this activity
if(posts == null) {
Log.w(TAG,"Did not receive any valid response body");
return;
}
for (COVID_Post_Data covid_post_data : posts) {
String content1,content2,content3,content4,content5 = "";
content1 = covid_post_data.getPositive() + "\n";
content2 = covid_post_data.getDeath() + "\n";
content3 = " (+" + covid_post_data.getPositiveIncrease() + ")\n";
content4 = " (+" + covid_post_data.getDeathIncrease() + ")\n";
content5 = " Updated : " + covid_post_data.getDate() + "\n";
textViewResult.append(content1);
textViewResult2.append(content2);
textViewResult3.append(content3);
textViewResult4.append(content4);
textViewResult5.append(content5);
}
}
2.get JSON file
public interface JsonPlaceHolderApi {
//get data from json about US infection
#GET("us/daily.json")
Call<List<COVID_Post_Data>> get_covid_post();
//get data from json about states infection
#GET("states/daily.json")
Call<List<COVID_Post_Data>> get_covid_post_state();
}
3.getter function
public class COVID_Post_Data {
private String dataChecked;
private int positiveIncrease;
private int negativeIncrease;
private int deathIncrease;
private String state;
private int positive;
private int death;
private int date;
//if the variable is matched with name in json file no need to put #SerializedName here
public String getDataChecked() {
return dataChecked;
}
public int getPositiveIncrease() {
return positiveIncrease;
}
public int getNegativeIncrease() {
return negativeIncrease;
}
public int getDeathIncrease() {
return deathIncrease;
}
public String getState() {
return state;
}
public int getPositive() {
return positive;
}
public int getDeath() { return death; }
public int getDate() {
return date;
}
}
I have ArrayList of strings. For each string, I want to call API with Retrofit2. And response of each API gives some data that I want to use and from there I want to call another API. Finally it will return string whether is success or not. I am able to achieve this but I want to all of the APIs should be called one after the other Like synchronous.
Here is my code looks like.
for (int i = 0; i < domainList.size(); i++) {
feedData3(domainList.get(i));
}
private void feedData3(String domain) {
Domain domain1 = new Domain();
streamFetch(domain)
.concatMap((Function<Contact, ObservableSource<?>>) contact -> {
domain1.setDomainData(contact.getDATA());
domain1.setDomain(domain);
return streamFetchData(domain1.getDomain(), domain1.getDomainData());
})
.subscribe(new Observer<Object>() {
#Override
public void onSubscribe(Disposable d) {
disposable = d;
}
#Override
public void onNext(Object o) {
Log.e(TAG, domain + ",Success");
}
#Override
public void onError(Throwable e) {
Log.e(TAG, domain + ",failure = " + e.getLocalizedMessage());
}
#Override
public void onComplete() {
}
});
}
public Observable<Contact> streamFetch(String domain) {
//GET API CALL
}
public Observable<String> streamFetchData(String domainName, String domainData) {
//POST API CALL
}
I want to implement Google Forms in my application. I have to make simple survey type forms as like Google Forms, I searching it from two days but I didn't get any specific document for Google Forms implementing in Android Application.
public class Fragment_Register extends Fragment {
View mainView;
public static final MediaType FORM_DATA_TYPE = MediaType.parse("application/x-www-form-urlencoded; charset=utf-8");
public static final String URL_FORM = "https://docs.google.com/forms/d/e/1FAIpQLSd7TT0S2pMwvRghAxAwDrJ42sWvI7uhqQBdl1WIyKRXjyWRWQ/formResponse";
public static final String NAME_KEY = "entry.2005620554";
public static final String EMAIL_KEY = "entry.1045781291";
public static final String NUMBER_KEY = "entry.1166974658";
public static final String COLLEGE_KEY="entry.75064397";
public static final String CHECK_KEY="entry.839337160";
public String Categories[]={"Category 1","Category 2","Category 3","Category 4"},category;
public String Events[]={"Event 1","Event 2","Event 3","Event 4"},event;
public String Events2[]={"Event2 1","Event2 2","Event2 3","Event2 4"};
private Context context;
private EditText editName;
private EditText editEmail;
private EditText editPhoneNumber;
private EditText editCollege;
private TextView select;
private CheckBox checkBox1,checkBox2;
private Spinner spinner;
private MultiSelectionSpinner spinner2;
private RelativeLayout layout;
ArrayAdapter<String> adapterCategoryCategory;
ArrayAdapter<String> adapterEventCategory;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
mainView=inflater.inflate(R.layout.fragment_fragment__register,container,false);
mainView.setTag("FOUR");
context = mainView.getContext();
Button SubmitButton = (Button)mainView. findViewById(R.id.button_register);
editName = (EditText)mainView. findViewById(R.id.editText_register_name);
editEmail = (EditText)mainView. findViewById(R.id.editText_register_email);
editPhoneNumber = (EditText)mainView. findViewById(R.id.editText_register_phone);
editCollege = (EditText)mainView. findViewById(R.id.editText_register_college);
spinner=(Spinner)mainView.findViewById(R.id.spinner);
spinner2=(MultiSelectionSpinner)mainView.findViewById(R.id.multiSpinner);
select=(TextView)mainView.findViewById(R.id.textViewSelect);
adapterCategoryCategory =new ArrayAdapter<String>(context,android.R.layout.simple_spinner_item, Categories);
adapterCategoryCategory.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(adapterCategoryCategory);
spinner.setPrompt("Select Category");
adapterEventCategory =new ArrayAdapter<String>(context,android.R.layout.simple_spinner_item, Events);
adapterEventCategory.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner2.setItems(Events);
spinner2.setPrompt("Select Event");
spinner2.setSelection(0);
SubmitButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
String st=spinner2.getSelectedItemsAsString();
Toast.makeText(context, st, Toast.LENGTH_SHORT).show();
Log.e("Selected",st);
}
});
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
category=adapterView.getItemAtPosition(i).toString();
if(i==0)
{
spinner2.setItems(Events);
}
else if (i==1)
{
spinner2.setItems(Events2);
}
}
#Override
public void onNothingSelected(AdapterView<?> adapterView) {
}
});
spinner2.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
event=adapterView.getItemAtPosition(i).toString();
}
#Override
public void onNothingSelected(AdapterView<?> adapterView) {
}
});
return mainView;
}
class PostDataTask extends AsyncTask<String, Void, Boolean> {
ProgressDialog progress;
#Override
protected void onPreExecute() {
progress = new ProgressDialog(context);
progress.setMessage("Please Wait..");
progress.show();
}
#Override
protected Boolean doInBackground(String... contactData) {
Boolean result = true;
String url = contactData[0];
String name = contactData[1];
String email = contactData[2];
String number = contactData[3];
String college = contactData[4];
String postBody = "";
try {
postBody = NAME_KEY + "=" + URLEncoder.encode(name, "UTF-8") +
"&" + EMAIL_KEY + "=" + URLEncoder.encode(email, "UTF-8") +
"&" + NUMBER_KEY + "=" + URLEncoder.encode(number, "UTF-8")+
"&" + COLLEGE_KEY + "=" + URLEncoder.encode(college, "UTF-8");
} catch (UnsupportedEncodingException ex) {
result = false;
}
try {
OkHttpClient client = new OkHttpClient();
RequestBody body = RequestBody.create(FORM_DATA_TYPE, postBody);
Request request = new Request.Builder()
.url(url)
.post(body)
.build();
Response response = client.newCall(request).execute();
} catch (IOException exception) {
result = false;
}
return result;
}
#Override
protected void onPostExecute(Boolean result) {
progress.cancel();
final AlertDialog.Builder alert=new AlertDialog.Builder(context);
alert.setMessage(result ? "Successfully Registered!" : "There was some error in sending message. Please try again after some time.").setCancelable(false).setPositiveButton("OK", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
editName.setText("");
editCollege.setText("");
editEmail.setText("");
editPhoneNumber.setText("");
}
});
AlertDialog alertDialog=alert.create();
alertDialog.show();
}
}
boolean validData()
{ String userName=editName.getText().toString();
String userNumber = editPhoneNumber.getText().toString();
String userEmail = editEmail.getText().toString();
String userCollege=editCollege.getText().toString();
if (userName.length()<3)
{
Toast.makeText(context, "Enter a Valid Name", Toast.LENGTH_SHORT).show();
return false;
}
if (userNumber.length()!=10||userNumber.startsWith("0")||userNumber.startsWith("1")||userNumber.startsWith("2")||userNumber.startsWith("3")||userNumber.startsWith("4")||userNumber.startsWith("5")||userNumber.startsWith("6"))
{
Toast.makeText(context, "Enter a Valid Number", Toast.LENGTH_SHORT).show();
return false;
}
if (userEmail.length()<3)
{
Toast.makeText(context, "Enter a Valid Email Address", Toast.LENGTH_SHORT).show();
return false;
}
if (userCollege.length()<3)
{
Toast.makeText(context, "Enter a Valid College Name", Toast.LENGTH_SHORT).show();
return false;
}
return true;
}
}
You can use a web view to render your google form within the application.
If for some reason you prefer not to use a web view, it is also possible to use the 'keys' in a google form(obtained via 'inspect element') to integrate the same with your application. Here is a link detailing the steps.
You can use the following in your onCreate():
progressDialog = new ProgressDialog(this);
progressDialog.setCancelable(false);
progressDialog.setMessage("Loading...");
queue = Volley.newRequestQueue(getApplicationContext());
submit.setOnClickListener(view -> {
postData(edit_name.getText().toString().trim(), edit_email.getText().toString().trim(),
edit_number.getText().toString().trim(), edit_remarks.getText().toString().trim());l
});
Also create a function in the activity as follows and in my Google Forms I have 4 entries to make which are name, phone, email and remark:
public void postData(final String name, final String email, final String phone, final String remark) {
progressDialog.show();
StringRequest request = new StringRequest(Request.Method.POST, Constants.url, new Response.Listener<String>() {
#Override
public void onResponse(String response) {
Log.d("TAG", "Response: " + response);
if (response.length() > 0) {
Snackbar.make(btn_submit, "Successfully Posted", Snackbar.LENGTH_LONG).show();
finish();
} else {
Snackbar.make(btn_submit, "Try Again", Snackbar.LENGTH_LONG).show();
}
progressDialog.dismiss();
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
progressDialog.dismiss();
Snackbar.make(btn_submit, "Error while Posting Data", Snackbar.LENGTH_LONG).show();
}
}) {
#Override
protected Map<String, String> getParams() {
Map<String, String> params = new HashMap<>();
params.put(Constants.nameField, name);
params.put(Constants.emailField, email);
params.put(Constants.phoneField,"+91"+phone);
params.put(Constants.remarkField, remark);
return params;
}
};
request.setRetryPolicy(new DefaultRetryPolicy(
50000,
DefaultRetryPolicy.DEFAULT_MAX_RETRIES,
DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
queue.add(request);
}
Create a Java class Constants where you enter the url and id of the entries in forms:
public class Constants {
// Google Forms URL .. replace with yours
public static final String url = "https://docs.google.com/forms/d/e/1FAIpQLSf65J3RmZS6QBg_TrsZZFx9s0l6109Q4E6PvUjuZ9go6D9l2g/formResponse";
// Google Form's Column ID
public static final String nameField = "entry.2006520554";
public static final String phoneField = "entry.1196974658";
public static final String emailField = "entry.1054781291";
public static final String remarkField = "entry.839339060"; }
Entry ids can be found by inspecting your Google Forms. For more details refer to the this link.
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.
}
});