Async-http often hangs. - android

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!

Related

How to correctly and cleanly implement Retrofit and MVVM pattern

I have a Fragment that logs in to an api like so:
btnLogin.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
try {
progressDialog.setTitle("Logging in...");
progressDialog.show();
viewModel.login(userName, password)
} catch (Exception e) {
e.printStackTrace();
}
}
});
and still in this fragment I observe the reply:
viewModel.getApiCallReply().observe(getActivity, new Observer<UserModel>() {
#Override
public void onChanged(UserModel userModel) {
progressDialog.dismiss();
Log.i(TAG, userModel.Id)
}
});
In my ViewModel I call a repository like so:
public MutableLiveDate<UserModel> getApiCallReply() { return repository.apiCallReply; }
public void login(String userName, String password) {
String uri = "http://172.0.0.1";
LoginModel loginModel = new LoginModel(userName, password);
Gson gson = new Gson();
String body = gson.toJson(loginModel);
repository.login(uri, body);
}
In my Repository I call the API using Retrofit2:
public MutableLiveData<UserModel> apiCallReply;
public MutableLiveData<UserModel> login(String url, String body) {
apiService.login(url, loginModel)
.enqueue(new Callback<UserModel>() {
#Override
public void onResponse(Call<UserModel> call, Response<UserModel> response) {
try {
if (response.isSuccessful()) {
apiCallReply.setValue(response.body());
} else {
String error = null;
try{
error = response.errorBody().string();
apiErrorCall.setValue(error);
} catch (IOException e) {
apiErrorCall.setValue(e.getMessage());
}
}
} catch (Exception e) {
e.printStackTrace();
apirErrorCall.setValue(e.getMessage());
}
}
#Override
public void onFailure(Call<UserModelAlprLogin> call, Throwable t) {
apirErrorCall.setValue(t.getMessage());
}
});
return alprLoginSuccessCall;
}
Is this the right way to do this?
Whenever I need to debug the viewModel.apiCallReply() in the fragment, starting from the button click, I have to start my trace from
[Fragment]btnLogin.setOnClickListener -> [Fragment]viewModel.login(userName, password) -> [ViewModel]repository.apiCallReply ->
[Repository]login -> [Repository]apiCallReply -> [ViewModel]getApiCallReply -> [Fragment]viewModel.getApiCallReply() -> [Fragment]public void onChanged(UserModel userModel)
I have to go through the whole cycle to get the result of the onChanged. It is ok if it is just one method but sometimes, the onChanged() event leads to another Retrofit call so I have to trace deeper.
I think I am doing it wrong coz, a clean programming practice makes the code easier to read, follow, and maintain; not the other way around.

Calling a function inside onRespons method

I am working on an Android app.
This is one function inside a fragment:
private void guardar_paciente() {
String tag_string_req = "req_login";
StringRequest strReq = new StringRequest(Request.Method.POST,
URL_CHECK, new Response.Listener<String>() {
#Override
public void onResponse(String response) {
try {
JSONObject jObj = new JSONObject(response);
boolean error = jObj.getBoolean("error");
String id_paciente = jObj.getString("id");
String id_nhc = jObj.getString("nhc");
if (!error) {
editor2.putString("id_paciente", id_paciente);
editor2.putString("nhc", id_nhc);
editor2.apply();
} else {
// Error in login. Get the error message
// String errorMsg = jObj.getString("error_msg");
}
} catch (JSONException e) {
// JSON error
e.printStackTrace();
// Toast.makeText(getActivity(), "Json error: " + e.getMessage(), Toast.LENGTH_LONG).show();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Toast.makeText(getActivity(),
error.getMessage(), Toast.LENGTH_LONG).show();
}
}) {
#Override
protected Map<String, String> getParams() {
// Posting parameters to login url
Map<String, String> params = new HashMap<String, String>();
params.put("nombre_completo", nombre_completo);
params.put("apellidos", apellidos);
params.put("tel1", tel1);
params.put("tel2", tel2);
params.put("email", email);
params.put("profesion", profesion);
params.put("sexo", sexo);
params.put("fecha_nacimiento", fecha_nacimiento);
params.put("edad", edad);
params.put("peso", peso);
params.put("talla", talla);
params.put("IMC", IMC);
params.put("consentimiento", "1");
params.put("clinica_paciente", clinica_actual);
params.put("imagen_paciente", imagen_paciente);
params.put("firma_paciente", numero+".JPG");
params.put("DNI", DNI);
params.put("direccion", direccion);
params.put("raza", raza);
return params;
}
};
// Adding request to request queue
AppController.getInstance().addToRequestQueue(strReq, tag_string_req);
}
What I need is to execute another function, abrirPaciente(), just after guardar_paciente() has finished all its methods and it is safe to change the UI.
I have tried calling abrirPaciente() just after editor2.apply();, but the app crashes.
Making use of callbacks :
public class foo {
interface ExampleInterface {
void finishedServiceCallNoErrors();
void finishedServiceCallWithError(String error);
}
void guardar_paciente(ExampleInterface exampleInterface) {
...
#Override
public void onResponse(String response) {
....
//there was no error
exampleInterface.finishedServiceCallNoErrors();
//there was an error
exampleInterface.finishedServiceCallWithError("your error message here");
....
}
...
}
}
and an example of how you'd make use of this :
public class bar implements foo.ExampleInterface {
//simple example of how you'd use this
private void callingIt() {
new foo().guardar_paciente(this); //here, we can pass `this` because our class is implementing the interface
}
//these now get returned to the class making use of the service call, so you can easily handle things here, instead of worrying about the logic in your service call response
#Override
public void finishedServiceCallNoErrors() {
//TODO("Handle the response with no error");
//your call completed and everything was fine, now do something else
}
#Override
public void finishedServiceCallWithError(String error) {
// TODO("Handle the response with an error")
// there was an error, handle it here
}
}
I'm not sure if this callback pattern will be safe to use if it's being triggered from a background thread, so for that you'd need to switch threads inside the callbacks, so inside finishedServiceCallNoErrors and inside finishedServiceCallWithError you'd potentially need like a Runnable, so you can make use of the main thread, or inside the onResponse of the service call, before triggering the callbacks, you could also change there to the main thread. You can find help with something like that here

Why does my progress bar not working properly?

I've been building an app which has Log In functionality. I've tested it but every time i tried to Log In, the progress bar disappeared to quickly (like a quarter second or something), and the response i get from the server is like about 2 seconds after the progress bar disappeared. Here are some of my codes.
My LoginTask inner class :
private class LoginTask extends AsyncTask<Account, Void, Account>{
private String getUsername = username.getText().toString();
private String getPassword = password.getText().toString();
#Override
protected void onPreExecute() {
super.onPreExecute();
//showDialog();
progressBar.setVisibility(View.VISIBLE);
}
#Override
protected void onPostExecute(Account account) {
super.onPostExecute(account);
//dismissDialog();
progressBar.setVisibility(View.GONE);
}
#Override
protected Account doInBackground(Account... accounts) {
getLogin(getUsername, getPassword);
return null;
}
}
Login Retrofit call to server
private void getLogin(String email, String password) {
Call<LoginAuth> call = userService.getLogin(email, password);
call.enqueue(new Callback<LoginAuth>() {
#Override
public void onResponse(Call<LoginAuth> call, Response<LoginAuth> response) {
try {
if (response.body().getToken_type().equals("xxx")) {
Log.i(TAG, "getLogin, Authorized access");
Log.i(TAG, "getLogin, access_token: " + response.body().getAccess_token().toString());
Log.i(TAG, "getLogin, expires_at" + response.body().getExpires_at().toString());
} else {
Log.e(TAG, "getLogin, Unauthorized access" + response.body().getToken_type().toString());
}
} catch (Exception e) {
Log.e(TAG, "getLogin exception " + e.getMessage());
}
}
#Override
public void onFailure(Call<LoginAuth> call, Throwable t) {
Log.e(TAG, "getLogin, onFailure : " + t.getMessage());
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
Toast.makeText(LoginActivity.this, "Unable to Log In :(", Toast.LENGTH_SHORT).show();
}
});
}
});
}
I want it to work like when the response is fetched, that's the time the progress bar disappeared (not instantly). Did i do something wrong with the code?
As you are using retrofit, there is no necessity to call your api in separate asynctask as retrofit manages it asynchronously. what you should do is show your progressbar before you call api and dismiss it in onResponse and onFailure both. so your code would change to something like below.
private void getLogin(String email, String password) {
progressBar.setVisibility(View.VISIBLE);
Call<LoginAuth> call = userService.getLogin(email, password);
call.enqueue(new Callback<LoginAuth>() {
#Override
public void onResponse(Call<LoginAuth> call, Response<LoginAuth> response) {
progressBar.setVisibility(View.Gone);
//rest of your code
}
#Override
public void onFailure(Call<LoginAuth> call, Throwable t) {
Log.e(TAG, "getLogin, onFailure : " + t.getMessage());
progressBar.setVisibility(View.Gone);
//rest of your code
}
});
}

web request not working on app first run

I have web request helper class in my app using OKHttp3 via standard async method call. everything just work fine, but in my Splash Activity just for first run (after new installation) web request calling not work! but if I close the app and run again everything work fine.
here is my call back interface:
public interface WebResult<T> {
void onValue(T value);}
here is calling method
public void getStatus(final WebResult result) {
urlBuilder.addQueryParameter("action", "test");
urlBuilder.addQueryParameter("reqbody", cd.toJSON());
String url = urlBuilder.build().toString();
Request request = new Request.Builder()
.header("Authorization", AuthKey)
.url(url)
.build();
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
setHasError(true);
setMsg(e.getMessage());
}
#Override
public void onResponse(Call call, final Response response) throws IOException {
if (!response.isSuccessful()) {
throw new IOException("Unexpected code " + response);
} else {
iAct.runOnUiThread(new Runnable() {
public void run() {
try {
String s = response.body().string();
ServerStat r = new ServerStat();
r.fromJSON(s);
result.onValue(r);
return;
} catch (IOException e) {
}
}
});
}
}
});
}
and its my splash activity
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
context = this;
WebHelper wh = new WebHelper(context);
wh.getStatus(new WebResult() {
#Override
public void onValue(Object value) {
ServerStat r = (ServerStat) value;
if (r.getErrorCode() == 0) {
Toast.makeText(context, r.getErrorMsg(), Toast.LENGTH_LONG).show();
} else {
Toast.makeText(context, r.getErrorMsg(), Toast.LENGTH_LONG).show();
}
}
});
}
I replaced OKHttp3 with google Volley and it's work in my case!

Null context in onResponse callback method retrofit android

I have a DialogFragment. When i click the OK button, i execute a retrofit call to my api like this
#Override
public void onResponse(Call<LoginResponse> call, Response<LoginResponse> response) {
int code = response.code();
if (code == 200) {
LoginResponse lr = response.body();
if (lr.getError()) {
Utils.showToast(getActivity(), "Error! Los datos son incorrectos.");
} else {
startActivity(new Intent(getActivity(), ConfigGeneralActivity.class));
}
} else {
Toast.makeText(getActivity(), "Error: " + String.valueOf(code), Toast.LENGTH_LONG).show();
}
}
at this part of the code, getActivity() is null.
Another option is to pass the context from the oncreatedialog to this callback, but when i do this, i got the error that says activity is not attached to fragment.
I know i can implement Otto but it's too complex for what i want to do. I just want to start a new activity from a retrofit callback inside a DialogFragment.
Any ideas? Thanks!
You need to runOnUiThread to get the Context inside OnResponse Callback
Like:
#Override
public void onResponse(Call call, Response response) throws IOException {
if (response.isSuccessful()) {
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
//your code
}
});
}
}

Categories

Resources