Android retrofit how to show No data message when empty json array - android

I am using retrofit to retrieve data from my server - it is a user search:
ApiInterface apiService = ApiClient.getClient().create(ApiInterface.class);
Call<List<Movie>> call = apiService.getSearch(lng, 0, newText);
call.enqueue(new Callback<List<Movie>>() {
#Override
public void onResponse(#NonNull Call<List<Movie>> call, #NonNull Response<List<Movie>> response) {
movieList = response.body();
if (dialog != null) {
dialog.dismiss(); dialog = null;
}
if (response.isSuccessful()) {
recyclerAdapter.setMovieList(movieList);
} else {
showMsgSnack(getString(R.string.Nodata));
}
}
#Override
public void onFailure(#NonNull Call<List<Movie>> call, #NonNull Throwable t) {
if (dialog != null) {
dialog.dismiss(); dialog = null;
}
if(t instanceof UnknownHostException){
showMsgSnack(getString(R.string.Network));
}
else if(t instanceof SocketTimeoutException){
showMsgSnack(getString(R.string.ServerTimeout));
}
else {
showMsgSnack(getString(R.string.ServerError));
}
}
});
Json is generaed via PHP in the server. If the search is success, json will return like [{"title": "sometitle",...}].
But if there is no match in database, I am returning just an empty {}.
But in android it goes to the failure and shows ServerError message. I want to show in that case some message as No match found.
How can I handle this response?
I also created a Default callback for Nullpointerexception, but this still doesn't work:
call.enqueue(new DefaultCallback<>(new Callback<List<Movie>>() {
#Override
public void onResponse(#NonNull Call<List<Movie>> call, #NonNull Response<List<Movie>> response) {
movieList = response.body();
if (dialog != null) {
dialog.dismiss();
dialog = null;
}
if (response.isSuccessful()) {
recyclerAdapter.setMovieList(movieList);
} else {
showMsgSnack(getString(R.string.Nodata));
}
}
#Override
public void onFailure(#NonNull Call<List<Movie>> call, #NonNull Throwable t) {
if (dialog != null) {
dialog.dismiss();
dialog = null;
}
if (t instanceof UnknownHostException) {
showMsgSnack(getString(R.string.Network));
}
if (t instanceof NullPointerException) {
showMsgSnack(getString(R.string.Nodata));
} else if (t instanceof SocketTimeoutException) {
showMsgSnack(getString(R.string.ServerTimeout));
} else {
showMsgSnack(getString(R.string.ServerError));
}
}
}));
}
public static class DefaultCallback<T> implements Callback<T> {
private static final String TAG = "YOUR_TAG";
private final Callback<T> callback;
public DefaultCallback(Callback<T> callback) {
this.callback = callback;
}
#Override
public void onResponse(#NonNull Call<T> call, Response<T> response) {
if (response.body() == null) {
callback.onFailure(call, new NullPointerException("Empty response"));
} else {
callback.onResponse(call, response);
}
}
#Override
public void onFailure(#NonNull Call<T> call, Throwable t) {
Log.e(TAG, t.toString());
callback.onFailure(call, t);
}
}
It says: YOUR_TAG: java.lang.IllegalStateException: Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 2 path $

In android after receiving response from API you want List of type Movie object (List) which is like [{"title": "sometitle",...}] this.
But when you don't find anything on search then you are returning " {} "
And here you are getting an issue, this is because you are excepting List means Array and API returning "{}" means Object.
Simple solution don't send object "{}" send Array "[]" and check
#Override
public void onResponse(#NonNull Call<List<Movie>> call, #NonNull Response<List<Movie>> response) {
movieList = response.body();
if (dialog != null) {
dialog.dismiss();
dialog = null;
}
if (movieList.size() > 0) {
recyclerAdapter.setMovieList(movieList);
} else {
showMsgSnack(getString(R.string.Nodata));
}
}
Hope you understand and this will help!

Related

Access Raw Response After Conversion

I've a backend that returns 200 HTTP code even if the request had errors:
{
"error": {
"message": "Generic error",
"code": 13000
}
}
Now, how can I access raw response body, after using:
.addConverterFactory(GsonConverterFactory.create(gson))
I've tried:
response.raw().body().string()
But I get:
java.lang.IllegalStateException: Cannot read raw response body of a converted body.
I've implemented a generic APICallback class, that should "block" the success and fire an APIError event:
abstract class APICallback<T> implements Callback<T> {
abstract void onSuccess(Call<T> call, T result);
#Override
public void onResponse(Call<T> call, Response<T> response) {
if (response.body() != null) {
if (response.body() instanceof APIError) {
// this is not working
} else {
onSuccess(call, response.body());
}
} else {
apiError = new APIError("Unknown error");
bus.post(new APIErrorEvent(apiError));
}
}
#Override
public void onFailure(#NonNull Call<T> call, #NonNull Throwable t) {
String message = t.getLocalizedMessage() != null ? t.getLocalizedMessage() : "Unknown error";
apiError = new APIError(message);
bus.post(new APIErrorEvent(apiError, source));
}
}
I'd like to mantain the "auto-converter" capability...many thanks in advance.
For error response you have to take response.errorBody() not response.body()
abstract class APICallback<T> implements Callback<T> {
abstract void onSuccess(Call<T> call, T result);
#Override
public void onResponse(Call<T> call, Response<T> response) {
if (response.body() != null) {
if (response.body() instanceof APIError) {
// this is not working
} else {
onSuccess(call, response.body());
}
} else {
apiError = new APIError("Unknown error");
bus.post(new APIErrorEvent(apiError));
}
}
#Override
public void onFailure(#NonNull Call<T> call, #NonNull Throwable t) {
String message = t.getLocalizedMessage() != null ? t.getLocalizedMessage() : "Unknown error";
apiError = new APIError(message);
bus.post(new APIErrorEvent(apiError, source));
}
}
change to
abstract class APICallback<T> implements Callback<T> {
abstract void onSuccess(Call<T> call, T result);
#Override
public void onResponse(Call<T> call, Response<T> response) {
if (response.isSuccessful()) {
if (response.body() != null) {
onSuccess(call, response.body());
}
} else {
if (response.errorBody() != null) {
if (response.errorBody() instanceof APIError) {
}
} else {
apiError = new APIError("Unknown error");
bus.post(new APIErrorEvent(apiError));
}
}
}
#Override
public void onFailure(#NonNull Call<T> call, #NonNull Throwable t) {
String message = t.getLocalizedMessage() != null ? t.getLocalizedMessage() : "Unknown error";
apiError = new APIError(message);
bus.post(new APIErrorEvent(apiError, source));
}
}

retrofit2 get json object without jsonArray name

I want to take jsonobject using retrofit without class definition, the result is null. How about this ?
I have a get query with JSON response :
[{"Kuota":"12"}]
This my code get data JSONObject .
public void GetKuota(String Key) {
IBookingService iBookingService = APIClient.getClient().create(IBookingService.class);
Call<ResponseBody> call = iBookingService.getKuota(Key);
call.enqueue(new Callback<ResponseBody>() {
#Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
if(!response.isSuccessful()) {
JSONObject jsonObject;
try {
jsonObject = new JSONObject(response.body().toString());
// Log.d(jsonObject.getString("Kuota"));
datas =String.valueOf(jsonObject.getInt("Kuota"));
} catch (JSONException e) {
e.printStackTrace();
}
}
}
#Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
}
});
}
Suggestion:
Create a model class, lets say Kuota
public class Kuota {
public int Kuota;
}
Then rewrite the Retrofit callback like this.
iBookingService.getKuota(Key).enqueue(new Callback<List<Kuota>>() {
#Override
public void onResponse(Call<List<Kuota>> call, Response<List<Kuota>> response) {
if(response.code() == 200 && response.body() != null) {
try {
for(Kuota k: response.body()) {
Log.d("Kuota Value" , " " + k.kuota);
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}
#Override
public void onFailure(Call<List<Kuota>> call, Throwable t) {
}
});
Replace iBookingService data holder from ResponseBody to List< Kuota >
if not added, please add the JSON serializer while setting up Retrofit instance.
new Retrofit.Builder()
.......
.addConverterFactory(GsonConverterFactory.create())
.build();
And also add the dependency
implementation 'com.squareup.retrofit2:converter-gson:2.4.0'

Retrofit return boolean value

I want to run changesHasBeenSaved if saveChanges function gives me true value.
How can I get boolean or string or integer, any value, when Retrofit finish?
Is it possible?
I need a function similar this:
public boolean saveChanges()
{
Boolean output = false;
RequestAPI requestAPI = Requests.getRetrofit();
Call<Object> jsonObjectCall = requestAPI.readAllCategoeies();
jsonObjectCall.enqueue(new Callback<Object>() {
#Override
public void onResponse(Call<Object> call, Response<Object> response) {
if(response.body() != null) {
output = true;
}
}
#Override
public void onFailure(Call<Object> call, Throwable t) {
call.cancel();
}
});
return output;
}
You should change method return type boolean to void as retrofit work asynchronous.
You should pass the listener/callback to get the status in callback.
First create callback interface like below
public interface ApiCallback {
void onResponse(boolean success);
}
Here how saveChanges will look like
public void saveChanges(final ApiCallback callback)
{
RequestAPI requestAPI = Requests.getRetrofit();
Call<Object> jsonObjectCall = requestAPI.readAllCategoeies();
jsonObjectCall.enqueue(new Callback<Object>() {
#Override
public void onResponse(Call<Object> call, Response<Object> response) {
callback.onResponse(response.body() != null);
}
#Override
public void onFailure(Call<Object> call, Throwable t) {
call.cancel();
callback.onResponse(false);
}
});
}
Then you need to call saveChanges method like below
saveChanges(new ApiCallback () {
public void onResponse(boolean success){
if(success){
// do something
} else{
// do something
}
}
});
jsonObjectCall.enqueue(new Callback<Object>() {
#Override
public void onResponse(Call<Object> call, Response<Object> response) {
//when the response is successful
if(response.isSuccessful()) {
output = true;
// display response as string
Log.i(TAG, "post submitted to API." + response.body().toString());
//Object user1 = response.body(); maps response to the modal class
}
}
#Override
public void onFailure(Call<Object> call, Throwable t) {
call.cancel();
}
})
Change you call back to like the above , included comments for clearity about usage .
Hope this helps.

how to make generic retrofit library for api calling

i'm working on API integration. i want to make generic class for API integration. which can comfortable with for all API integration.right now i'm using separate code for all API. i'm new in android application development. so please guide me.
public void getHomeCategoryDetailApi(Context context) {
final ProgressDialog loadingDialog = ProgressDialog.show(context, "Please wait", "Loading...");
Retrofit restAdapter = ApiLists.retrofit;
ApiLists apiCall = restAdapter.create(ApiLists.class);
Call<HomeCategoryModelClass> call = apiCall.homePageCatListAPI();
Log.d(TAG, "CategoryDetail : " + call.request()+" \n"+apiCall.homePageCatListAPI().toString());
call.enqueue(new Callback<HomeCategoryModelClass>() {
#Override
public void onResponse(Call<HomeCategoryModelClass> call, Response<HomeCategoryModelClass> response) {
Log.d(TAG, "onResponse: CategoryDetail:" + response.body());
Log.d(TAG, "onResponse: response.code():" + response.code());
if (response.body() == null) {
loadingDialog.dismiss();
globalClass.showAlertDialog(getActivity(), getString(R.string.InternetAlert), getString(R.string.InternetMessage), false);
} else {
loadingDialog.dismiss();
if (response.body().getStatusCode().equalsIgnoreCase("1")) {
homeCategoryImageMenu = (ArrayList<Menu>) response.body().getMenu();
thirdHorizontalRecyclerAdapter.notifyDataSetChanged();
} else {
globalClass.showAlertDialog(getActivity(), "Alert", "" + response.body().getStatus(), false);
}
}
if (response.errorBody() != null) {
try {
Log.d(TAG, "onResponse: response.errorBody()===>" + response.errorBody().string());
if (loadingDialog.isShowing() && loadingDialog != null) {
loadingDialog.dismiss();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
#Override
public void onFailure(Call<HomeCategoryModelClass> result, Throwable t) {
Log.d(TAG, "onFailure: " + result.toString());
loadingDialog.dismiss();
globalClass.showAlertDialog(getActivity(), getString(R.string.InternetAlert), getString(R.string.InternetMessage), false);
}
});
}
Here is Bast Way to call API
public class APIResponse {
private static String TAG = APIResponse.class.getSimpleName();
public static <T> void callRetrofit(Call<T> call, final String strApiName, Context context, final ApiListener apiListener) {
final ProgressDialog progressDialog = new ProgressDialog(context);
progressDialog.setMessage("Loading...");
progressDialog.setCancelable(false);
progressDialog.show();
call.enqueue(new Callback<T>() {
#Override
public void onResponse(Call<T> call, Response<T> response) {
if (strApiName.equalsIgnoreCase("LoginApi")) {
if (response.isSuccessful()) {
Log.d(TAG, "onResponse: " + response.body().toString());
// NearByNurse nearByNurse = (NearByNurse) response.body(); // use the user object for the other fields
// apiListener.success(url,nearByNurse);
progressDialog.dismiss();
} else {
try {
Log.d(TAG, "onResponse: " + response.errorBody().string());
apiListener.error(strApiName, response.errorBody().string());
progressDialog.dismiss();
} catch (IOException e) {
e.printStackTrace();
}
}
} else if (strApiName.equalsIgnoreCase("")) {
//Patient user = (Patient) response.body();
}
}
#Override
public void onFailure(Call<T> call, Throwable t) {
Log.d(TAG, "onFailure: " + t.toString());
if (strApiName.equalsIgnoreCase("searchNearbyTest")) {
apiListener.failure(strApiName, t.toString());
}
progressDialog.dismiss();
}
});
}
In API Calling Side
private void loginApi() {
Retrofit retrofit = ApiLists.retrofit;
ApiLists apiList = retrofit.create(ApiLists.class);
Call<JsonElement> loginApiCall = apiList.loginApi("kjdf", "fkldngdkl", "lkfdxngl", "kjngn", "jksdgkj");
APIResponse.callRetrofit(loginApiCall, "LoginApi", LoginActivity.this, this);
}
#Override
public void success(String strApiName, Object response) {
if (strApiName.equals("LoginApi")) {
}
}
#Override
public void error(String strApiName, String error) {
if (strApiName.equals("LoginApi")) {
}
}
#Override
public void failure(String strApiName, String message) {
if (strApiName.equals("LoginApi")) {
}
and interface call on API response.
public interface ApiListener {
void success(String strApiName, Object response);
void error(String strApiName, String error);
void failure(String strApiName, String message);
}
This's my common function basic call Api.java
public class Api {
private void basicCall(Call<DataResponse> call) {
if (call == null) {
listener.onResponseCompleted(Config.STATUS_404, "404 not found", null);
return;
}
call.enqueue(new Callback<DataResponse>() {
#Override
public void onResponse(#NonNull Call<DataResponse> call, #NonNull Response<DataResponse> response) {
int code = response.code();
//Check http ok
if (code == HttpURLConnection.HTTP_OK) {
//Check status
if (response.body().getStatus() == Config.STATUS_OK) {
//Everything's OK
listener.onResponseCompleted(Config.STATUS_OK, response.body().getError(), response.body().getData());
} else {
listener.onResponseCompleted(Config.STATUS_FAILED, response.body().getError(), null);
}
} else if (code == HttpURLConnection.HTTP_UNAUTHORIZED) {
try {
ErrorResponse error = Api.gson.fromJson(response.errorBody().string(), ErrorResponse.class);
listener.onResponseCompleted(Config.STATUS_401, error.getError(), error.getData());
} catch (IOException e) {
e.printStackTrace();
}
} else {
listener.onResponseCompleted(Config.STATUS_404, "404 not found", null);
}
}
#Override
public void onFailure(#NonNull Call<DataResponse> call, #NonNull Throwable t) {
listener.onResponseCompleted(Config.STATUS_404, "404 not found", null);
}
});
}
//And you can use
public void getProductList(OnResponseCompleted listener) {
this.listener = listener;
Call<DataResponse> call = apiService.getProductList();
basicCall(call);
}
}
//or orther function
This's ApiService.java
public interface ApiInterface {
#POST("product/list")
Call<DataResponse> getProductList();
}
This's OnResponseCompleted.java
public interface OnResponseCompleted {
void onResponseCompleted(int status, String error, Object data);
}
i want to make like this .i just pass some require parameter....
public void showAlertDialog(Context context, String title, String message,
Boolean status) {
AlertDialog alertDialog = new AlertDialog.Builder(context).create();
alertDialog.setTitle(title);
// Set Dialog Message
alertDialog.setMessage(message);
alertDialog.setCancelable(false);
if (status != null)
// Set alert dialog icon
alertDialog.setIcon((status) ? R.drawable.ic_success : R.drawable.ic_fail);
// Set OK Button
alertDialog.setButton("OK", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
}
});
// Show Alert Message
alertDialog.show();
}
Try this code..
In this code Retrofit object set up done in one class and all api calling into interface..
public class ApiClient {
private final static String BASE_URL = "https://api.github.com";
public static ApiClient apiClient;
private Retrofit retrofit = null;
public static ApiClient getInstance() {
if (apiClient == null) {
apiClient = new ApiClient();
}
return apiClient;
}
//private static Retrofit storeRetrofit = null;
public Retrofit getClient() {
return getClient(null);
}
private Retrofit getClient(final Context context) {
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient.Builder client = new OkHttpClient.Builder();
client.readTimeout(60, TimeUnit.SECONDS);
client.writeTimeout(60, TimeUnit.SECONDS);
client.connectTimeout(60, TimeUnit.SECONDS);
client.addInterceptor(interceptor);
client.addInterceptor(new Interceptor() {
#Override
public okhttp3.Response intercept(Chain chain) throws IOException {
Request request = chain.request();
return chain.proceed(request);
}
});
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.client(client.build())
.addConverterFactory(GsonConverterFactory.create())
.build();
return retrofit;
}
}
after that call the api into api interface..
public interface ApiInterface {
#GET("{affenpinscher}/images")
Call<Product> getProductData(#Path("affenpinscher") String breed);
#GET("getProductDetailByProductId?ProductId=3")
Call<JsonObject> ITEM_DESCRIPTION_RESPONSE_CALL();
#POST("linke")
Call<Response> passJsonData(#Body JsonData jsonData);
#GET("/users/waadalkatheri/repos")
Call<Response> getdata();
}
and when you call api in activity or fragment used below code..
ApiInterface apiInterface = ApiClient.getInstance().getClient().create(ApiInterface.class);
Call<ResponseData> responseCall = apiInterface.getdata();
responseCall.enqueue(new Callback<ResponseData>() {
#Override
public void onResponse(Call<ResponseData> call, retrofit2.Response<ResponseData> response) {
if (response.isSuccessful() && response.body() != null && response != null) {
Toast.makeText(getApplicationContext(), "GetData" + response.body().getLanguage(), Toast.LENGTH_SHORT).show();
}
}
#Override
public void onFailure(Call<ResponseData> call, Throwable t) {
Log.d("Errror", t.getMessage());
}
});

Parsing through Retrofit and set Data to the Base Adapter

I am working with Retrofit (JSON Parsing Android) and I am new with this I am so confused to set the response with the Base Adapter.
This is my Example Class
public class Example {
#SerializedName("Totalrecord")
#Expose
private List<Totalrecord> totalrecord = null;
#SerializedName("Table1")
#Expose
private List<Table1> table1 = null;
#SerializedName("Table2")
#Expose
private List<Table2> table2 = null;
public List<Totalrecord> getTotalrecord() {
return totalrecord;
}
public void setTotalrecord(List<Totalrecord> totalrecord) {
this.totalrecord = totalrecord;
}
public List<Table1> getTable1() {
return table1;
}
public void setTable1(List<Table1> table1) {
this.table1 = table1;
}
public List<Table2> getTable2() {
return table2;
}
public void setTable2(List<Table2> table2) {
this.table2 = table2;
}
}
Here I am trying to retrieve Response in ArrayList so that I can set it to Adapter
ApiService api = RetroClient.getApiService();
Call<Example> call = api.getMyJson(38, 109);
call.enqueue(new Callback<Example>() {
#Override
public void onResponse(Call<Example> call, Response<Example> response) {
dialog.dismiss();
if (response.isSuccessful()) {
// Response_list = response.body().get...
adapter_view = new Adapter_view(Response_list, MainActivity.this);
listView.setAdapter(adapter_view);
}
}
#Override
public void onFailure(Call<Example> call, Throwable t) {
dialog.dismiss();
System.out.println("failed");
}
});
But I am confused to set The type of ArrayList and also Type of Callback<> so that I can get the response properly and set ArrayList to the Adapter.
try following if you want to set List<Totalrecord> in your adapter.
ApiService api = RetroClient.getApiService();
Call<Example> call = api.getMyJson(38, 109);
call.enqueue(new Callback<Example>() {
#Override
public void onResponse(Call<Example> call, Response<Example> response) {
dialog.dismiss();
if (response.isSuccessful()) {
Response_list.clear();
Response_list.addAll(call.getTotalrecord());
adapter_view = new Adapter_view(Response_list, MainActivity.this);
listView.setAdapter(adapter_view);
}
}
#Override
public void onFailure(Call<Example> call, Throwable t) {
dialog.dismiss();
System.out.println("failed");
}
});
List<Table1> mList=new ArrayList();
ApiService api = RetroClient.getApiService();
Call<Example> call = api.getMyJson(38, 109);
call.enqueue(new Callback<Example>() {
#Override
public void onResponse(Call<Example> call, Response<Example> response) {
dialog.dismiss();
if (response.body.isSuccessful()) {
mList=response.body.getTable1();
adapter_view = new Adapter_view(mList,MainActivity.this);
listView.setAdapter(adapter_view);
}
}
#Override
public void onFailure(Call<Example> call, Throwable t) {
dialog.dismiss();
System.out.println("failed");
}
});

Categories

Resources