I created a default login activity when creating a new project in Android.
By default creates a progressBar that works well to login and displayed correctly. All this makes it through AsyncTask.
Now I used Retrofit 2 to make way Async login.
When I pushed the button login launched a method that content:
....
....
showProgress(true);
call.enqueue(new Callback<User>() {
#Override
public void onResponse(Call<User> call, retrofit2.Response<User> response) {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
User userdatos = response.body();
if(userdatos != null){
// redirect to Main Activity page
showProgress(false);
Intent i = new Intent(LoginActivity.this, MainActivity.class);
startActivity(i);
}
else{
showProgress(false);
mPasswordView.setError(getString(R.string.error_incorrect_password));
mPasswordView.requestFocus();
}
}
#Override
public void onFailure(Call<User> call, Throwable t) {
call.cancel();
showProgress(false);
Toast.makeText(LoginActivity.this, "Please check your network connection and internet permission", Toast.LENGTH_LONG).show();
}
});
I forced a Thread.sleep (3000) to check.
No progress bar appears, and the debug shows:
Skipped 180 frames! The application May be doing too much work on ITS main thread.
How can this be solved?
I will have many Async calls where I would always display a progress bar to wait for the response is complete.
Thank you
Let me share with you mine code implementation for default Login activity created by Android Studio and my Retrofit implementation. Default login activity, consist AsyncTask which called UserLoginTask, inside it, I did all my login logic stuff. I am using .execute instead of .enqueue because it synchronous and we don't need new thread inside AsyncTask
public class UserLoginTask extends AsyncTask<Void, Void, Boolean> {
private final String mEmail;
private final String mPassword;
UserLoginTask(String email, String password) {
mEmail = email;
mPassword = password;
}
#Override
protected Boolean doInBackground(Void... params) {
Retrofit restAdapter = new Retrofit.Builder()
.baseUrl(Constants.ROOT_API_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
IConstructSecureAPI service = restAdapter.create(IConstructSecureAPI.class);
Call<JsonElement> result = service.getToken(mEmail, mPassword, "password");
try {
//using sync request and get response
JsonElement element = result.execute().body();
if(element!=null) {
//do whatever you want with your response
}
} catch (IOException e) {
e.printStackTrace();
return false;
}
return true;
}
#Override
protected void onPostExecute(final Boolean success) {
mAuthTask = null;
showProgress(false);
if (success) {
if(clientType != 2){
mPasswordView.setError(getString(R.string.error_incorrect_password));
mPasswordView.requestFocus();
clientType = 0;
}
else {
Intent intent = new Intent(getApplicationContext(), MainActivity.class);
startActivityForResult(intent, 1);
}
// finish();
} else {
mPasswordView.setError(getString(R.string.error_incorrect_password));
mPasswordView.requestFocus();
}
}
#Override
protected void onCancelled() {
mAuthTask = null;
showProgress(false);
}
}
Shameless Promotion
I've created RxLoading library for that, it can do this and much more,
you can just do something like this:
networkCall().compose(RxLoading.<>create(loadingLayout)).subscribe(...);
it consists out of 2 classes, a custom view (loadingLayout) and RxLoading which is a transformer that glue it all together, you can choose to work with both or either of them.
you need to be using retrofit2 with RxJava though.
RxLoading also supports empty and error states (with built-in retry mechanism, so make sure to check this options as well)
you can find out more on the GitHub page.
Related
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
I have an app in which user authentication is done sending a request and if response is successful the app navigated to next activity.If username password is correct it navigates but if it is incorrect the dialog box pops up and does not close and in my logs I get
java.io.IOException: HTTP/1.1 401 The username or password you entered is incorrect.
I have handled it in my code that if response is not successful display a toast message Login failed and dismiss dialog box.if I enter incorrect credentials it is not showing it.That part of the code is unreachable I checked using breakpoint.
My code:
buttonlogin.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
if(etemail.getText().toString().equals("") && etpass.getText().toString().equals(""))
{
Toast.makeText(getApplicationContext(),"Enter your username and Password",Toast.LENGTH_LONG).show();
}
else
{
final ProgressDialog progressDialog = new ProgressDialog(MainActivity.this);
progressDialog.setMessage("Logging in..Please wait");
progressDialog.setIndeterminate(true);
progressDialog.show();
new Thread(new Runnable()
{
public void run()
{
try
{
restApi = new RallyRestApi(new URI("https://rally1.rallydev.com"),etemail.getText().toString(),etpass.getText().toString());
restApi.setApplicationName("BMC-AndroidforRally");
qtestset = new QueryRequest("Users");
qtestset.setLimit(1);
response=restApi.query(qtestset);
if(!response.wasSuccessful())
{
parent.runOnUiThread(new Runnable() {
#Override
public void run()
{
Toast.makeText(getApplicationContext(),"Login Failed..incorrect Username/Password",Toast.LENGTH_LONG).show();
progressDialog.dismiss();
}
});
}
else
{
progressDialog.dismiss();
//Toast.makeText(getApplicationContext(),"Login Successful.....Logged in as :"+etemail.getText().toString(),Toast.LENGTH_LONG).show();
Intent mainintent = new Intent(MainActivity.this,FirstPage.class);
mainintent.putExtra("username",etemail.getText().toString());
mainintent.putExtra("password",etpass.getText().toString());
startActivity(mainintent);
}
}
catch (URISyntaxException | IOException e)
{
e.printStackTrace();
}
}
}).start();
}
}
});
Also i have created a new thread for faster execution.
In this concepts no need to implement runnable thread.
Remove Thread in OnClickListener it will work correctly.Because of thread only its showing old message(still that running). Best remove thread in OnClickListener or stop thread when condition going to false statement.
im using OptimusHTTP library on my android project. im trying to show loading if my app is contacting server. my problem is why my progress dialog does not dismiss. here is my code.
public void connectREST()
{
//using data json dummy
String SERVER = "http://jsonplaceholder.typicode.com/posts/1";
OptimusHTTP client = new OptimusHTTP();
client.enableDebugging();
client.setMethod(OptimusHTTP.METHOD_GET);
//parameter
ArrayMap<String, String> params = new ArrayMap<>();
params.put("email", "abc#abc.com");
params.put("pass", "abc");
//make request
ArrayList<HttpReq> refHttpReqList = new ArrayList<>();
try {
//mprogressdialog.show(this, "", "Loading", true);
// makeRequest() returns the reference of the request made
// which can be used later to call the cancelReq() if required
// if no request is made the makeRequest() returns null
HttpReq req = client.makeRequest(MainActivity.this, SERVER, params, responseListener);
if (req != null)
refHttpReqList.add(req);
mprogressdialog.show(this, "Loading", "Wait while loading...");
if (mprogressdialog != null && mprogressdialog.isShowing()) {
mprogressdialog.dismiss();
}
} catch (Exception e) {
e.printStackTrace();
}
}
private final OptimusHTTP.ResponseListener responseListener = new OptimusHTTP.ResponseListener() {
#Override
public void onSuccess(String msg) {
System.out.println(msg);
//mprogressdialog.dismiss();
Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_LONG).show();
}
#Override
public void onFailure(String msg) {
System.out.println(msg);
}
};
i know that this library (OptimusHTTP) is using asnyc when contacting server.
is there any configuration whether im using sync or async on the http connection ?
what if im include get method in some async code (double async) ?
i know that my question seem like some newbie question. but it takes learn process to become a pro :) Thanks.
#navotera : You can show a ProgressDialogue just before making a request and when the request completes , under the listener just dismiss the progress dialogue.
i.e
...
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
...
...
// Initialize the progressdialog
final ProgressDialog progressDialog = new ProgressDialog(this);
progressDialog.setMessage("Connecting");
...
...
// Show the progressdialog just before making a request
progressDialog.show();
// Make the request
req = client.makeRequest(MainActivity.this, SERVER_URL, params,new OptimusHTTP.ResponseListener(){
#Override public void onSuccess(String msg) {
System.out.println(msg);
// Dismiss the progressdialog
progressDialog.dismiss();
}
#Override public void onFailure(String msg) {
System.out.println(msg);
// Dismiss the progressdialog
progressDialog.dismiss();
}
});
You see this library (OptimusHTTP) is using asnyc when contacting server.But why did you update UI on the same thread ?
private final OptimusHTTP.ResponseListener responseListener = new OptimusHTTP.ResponseListener() {
#Override
public void onSuccess(String msg) {
System.out.println(msg);
//mprogressdialog.dismiss();
Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_LONG).show();
}
#Override
public void onFailure(String msg) {
System.out.println(msg);
}
};
Hi In my Application I'm sending one request to server to validate the user,after sending the request I'm storing that value in database and making the status as 1, after some time I'm changing the status to 2 in database.Now my android app should wait till the status becomes 2. For this I'm showing the user in mobile progress bar.But my problem is as soon as I send the request progress bar stops displaying in the mobile.
Here is what I have tried.
progressDialog = new ProgressDialog(MainActivity.this);
progressDialog.show();
progressDialog.setTitle("Please Wait");
progressDialog.setMax(100);
e1 = edittext.getText().toString();
//Toast.makeText(MainActivity.this, "" + e1, Toast.LENGTH_SHORT).show();
AsyncHttpClient client = new AsyncHttpClient();
final RequestParams params = new RequestParams();
params.put("sendingJSON", composeJSON());
client.post("http://192.168.43.137/gpstracker/check_user.php", params, new AsyncHttpResponseHandler() {
public void onSuccess(String response) {
Gson gson = new GsonBuilder().create();
try {
progressDialog.dismiss();
JSONArray arr = new JSONArray(response);
for (int i = 0; i < arr.length(); i++) {
JSONObject obj = (JSONObject) arr.get(i);
String general = obj.get("success").toString();
Toast.makeText(getApplicationContext(), ""+general, Toast.LENGTH_LONG).show();
}
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void onFailure(int statusCode, Throwable error, String content) {
if (statusCode == 404) {
Toast.makeText(getApplicationContext(), "Requested resource not found", Toast.LENGTH_LONG).show();
} else if (statusCode == 500) {
Toast.makeText(getApplicationContext(), "Something went wrong at server end", Toast.LENGTH_LONG).show();
} else {
Toast.makeText(getApplicationContext(), "Unexpected Error occcured! [Most common Error: Device might not be connected to Internet]",Toast.LENGTH_LONG).show();
}
}
});
}
});
alert.setNegativeButton("Cancel", new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface dialog, int whichButton) {
// what ever you want to do with No option.
}
}
);
alert.show();
}
A good solution in this cases is to work with events. Using events you can notify everywhere that something is happening. In your case, you can send an event when "the status changes to 2 in the database" and dismiss the progress dialog. There is a powerful and easy-to-use library to work with events called eventbus:
https://github.com/greenrobot/EventBus
I hope this helps to clarify!
I would recomend you to use an AsynkTask to do it. You can create the progress dialog in the onPreExecute() method, do what you are trying to do (post action or database update) in doInBackground() and once you have everything you want, in the onPostExecute() you can dismiss the dialog and send the result/data, if you need to, to your activity.
public class yourAsyncTask extends AsynkTask<Params, Params, Params>
#Override
protected void onPreExecute() {
super.onPreExecute();
pDialog = new ProgressDialog(context);
pDialog.setMessage("Charging...");
pDialog.setCancelable(true);
pDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
#Override
public void onCancel(DialogInterface dialog) {
errorMesaje = "Process cancelled";
cancel(true);
}
});
pDialog.show();
}
#Override
protected Void doInBackground(Params... params) {
//Do your things here...
#Override
protected void onPostExecute(Void void) {
super.onPostExecute(void);
//Use an interface to call your activity and pass the data, you will have to change the attribute of the method in order to do it.
// Dismiss your dialog when your requests have finished
pDialog.dismiss();
}
I donĀ“t understand what are you trying to do exactly, if you just want to be sure that the post action is completed before the progress bar goes off, this solution will be fine. You can also call in the doInBackground() to your database and update it.
Hope it's clear enough!
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!