I am working on a calculator app for my first project with android. Some of the calculations take a while because of how many operands are put in the equation. I'm using mxparser api to evaluate the equation. A progressDialog is supposed to show during my asyncTask, but it only shows for a fraction of a second if at all.
I have tried other solutions on stackoverflow and nothing so far has worked. Any ideas as to why this is occurring?
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private EditText editScreen;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
editScreen = (EditText) findViewById(R.id.calculator_display);
Button btnEq = (Button) findViewById(R.id.btn_equals);
btnEq.setOnClickListener(this);
}
#Override
public void onClick(View v) {
String equation = editScreen.getText().toString();
switch (v.getId()) {
case R.id.btn_equals:
doCalculation(equation);
break;
default:
Log.e("ERROR", "button was not implemented: " + v.getId());
}
}
private void doCalculation(String equation) {
Expression expression = new Expression(equation);
boolean isCorrectSyntax = expression.checkSyntax();
if (isCorrectSyntax) {
new calcAsync(MainActivity.this, equation).execute(expression);
} else {
Toast.makeText(getApplicationContext(), "Error in expression, please check syntax", Toast.LENGTH_LONG).show();
}
}
private class calcAsync extends AsyncTask<Expression, Void, Void> {
private ProgressDialog dialog;
private String equation;
private String answer;
public calcAsync(Context context, String equation) {
this.equation = equation;
}
#Override
protected Void doInBackground(Expression... params) {
Log.d("DEV", "starting calculation");
Double result = params[0].calculate();
String[] r = result.toString().split("\\.");
if (result.equals(Double.NaN) || result.equals(Double.NEGATIVE_INFINITY) || result.equals(Double.POSITIVE_INFINITY)) {
answer = result.toString();
} else if (r[1].length() == 1 && Integer.parseInt(r[1]) == 0) {
answer = r[0];
} else {
answer = result.toString();
}
return null;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
dialog = new ProgressDialog(MainActivity.this);
dialog.setIndeterminate(true);
dialog.setCancelable(false);
dialog.setMessage("Calculating...");
dialog.setTitle("Please wait...");
dialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
dialog.show();
Log.d("DEV", "dialog shown");
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
if (dialog != null) {
Log.d("DEV", String.valueOf(dialog.isShowing()));
dialog.dismiss();
Log.d("DEV", "Dialog dismissed");
}
editScreen.setText(answer);
saveToHistory(equation, answer);
}
}
}
Update 7/14/16:
It seems that the actual calculation itself doesn't take long at all. But from the time it takes after the equals button is pressed, until the result is displayed takes a while. 3 seconds for a long string of 1+1... (33 additions in my testing), longer if the string is longer than that.
Update 7/18/16:
I found out why my progressDialog wasn't showing. In the doCalculation method I am checking the syntax with equation.checkSyntax() before the asyncTask. This is where my delay was coming from, so the asyncTask wasn't doing the bulk of the work. after adding it in the asyncTask, I noticed that it takes considerably longer for the same test case as my previous update.
If you know that the delay occurs from the time the equals button is pressed, why not move the ProgressDialog to start there instead?
Related
I have the below code working fine to find and read a record in a Room database via an id. Android Studio required adding a try/catch block which I've included below.
Two questions:
Can I leave the if{} section blank in onPostExecute() if there is no Exception?
How do I show an AlertDialog here without leaking contect and without using a hack via WeakReference?
// AsyncTask for reading an existing CardView from the database.
private static class ReadAsyncTask extends AsyncTask<Integer, Void, Quickcard> {
private QuickcardDao asyncTaskDao;
Exception e;
ReadAsyncTask(QuickcardDao dao) {
asyncTaskDao = dao;
}
#Override
public Quickcard doInBackground(final Integer... params) {
Quickcard result;
try {
result = asyncTaskDao.readCardForUpdate(params[0]);
} catch (Exception e) {
this.e = e;
result = null;
}
return result;
}
#Override
protected void onPostExecute(Quickcard quickcard) {
if (e == null) {
// *** Okay to leave this blank? If not, what should go here?
}
else {
// *** How do I show an AlertDialog here with leaking context?
}
}
}
You cannot use a view object while using a thread that operates in background.. U must implement the dialog in the UI thread. When you are implementing the the asynchronous class, in that method you should show the alert dialog. Hope this helps..
This is what I will do to prevent leaks from happening.
private static class showDialog extends AsyncTask<String, String, String> {
private WeakReference<MainActivity> mainActivityWeakReference;
showDialog(MainActivity mainActivity){
this.mainActivityWeakReference = new WeakReference<>(mainActivity);
}
#Override
protected String doInBackground(String... params) {
//do your long long time consuming tasks here.
return "Done";
}
#Override
protected void onPostExecute(String result) {
// execution of result of Long time consuming operation
//Just building an alert dialog to show.
if (mainActivityWeakReference.get() != null){
final MainActivity activity = mainActivityWeakReference.get();
new AlertDialog.Builder(activity).setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
Toast.makeText(activity, "Yes was clicked", Toast.LENGTH_SHORT).show();
}
}).show();
}
}
#Override
protected void onPreExecute() {
}
#Override
protected void onProgressUpdate(String... text) {
}
}
I have a ListFragment which has an AsyncTask in it to write data to a remote store. I need to have a ProgressDialog show status while the data is being sent since this may take a considerable time depending on the number of files being stored remotely. I have done this successfully from an Activity, but I am having issues showing progress within the ListFragments AsyncTask.
This is complicated by the fact that I need to show updates within the doInBackground method of the task, since that is where the major of the work is being done. That said, the ProgressDialog is not showing up at all even in the non-UI bound onPreExecute() method. Looking at other posts for ProgressDialogs I am using passing getActivity() to the ProgressDialog. Also this mechanism is working with several other Activity classes I am using else where, just not here. I am probably just missing something obvious so any help is appreciated.
Here is a code example - forgive me if it does not compile or has a mistake - I had to remove boatloads of code to boil it down to the problem at hand:
public class MyFragment extends ListFragment {
private ProgressDialog mProgress;
private void hideProgress() {
if (mProgress != null) {
mProgress.dismiss();
mProgress = null;
}
}
private void showProgress(String message) {
if (mProgress != null) {
mProgress.dismiss();
mProgress = null;
}
mProgress = ProgressDialog.show(getActivity(), null, message, true, false);
}
protected void updateProgressMessage(String message) {
if (mProgress != null && mProgress.isShowing()) {
mProgress.setMessage(message);
}
}
public syncForms() {
new syncPendingFormsResultTask().execute();
}
private class syncTask extends AsyncTask<Object, String, Boolean> {
#Override
protected void onCancelled() {
hideProgress();
}
#Override
protected void onPreExecute() {
showProgress("Submitting Form...");
}
#Override
protected Boolean doInBackground(Object... params) {
onProgressUpdate("Uploading Form");
}
#Override
protected void onProgressUpdate(String... values) {
String message = values[0];
updateProgressMessage(message);
}
#Override
protected void onPostExecute(Boolean result) {
showProgress("Upload Complete...");
hideProgress();
}
}
}
}
The syncForms() is the method called to initiate the task.
I have a huge database (40MB) on an SDCard. I need fetch data, with LIKE in query, which is very slow.
DB request takes about 5 seconds. Therefore, I need to do it asynchronously and with ProgressDialog.
I tried it with AsyncTask, but problem is with ProgressDialog. It was implemented this way:
private class GetDataFromLangDB extends AsyncTask<String, String, String> {
private final ProgressDialog dialog = new ProgressDialog(TranslAndActivity.this);
#Override
protected void onPreExecute() {
super.onPreExecute();
urDBCursor.close();
curDBCursor = null;
scaAdapter = null;
this.dialog.setMessage("Loading data...");
this.dialog.show();
}
#Override
protected String doInBackground(String... whatSearch) {
String result = "";
if (myDatabaseAdapter != null) {
curDBCursor = myDatabaseAdapter.fetchAll(whatSearch[0]);
}
return result;
}
#Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
if (this.dialog.isShowing()) {
this.dialog.dismiss();
}
prepareListView();
}
}
The problem is that ProgressDialog is not shown during the DB request.
After finished database query, it flash on screen for a short time. When user tries
to tap on screen during database request, UI is freezed, and after DB request
message about 'not responding' is shown.
I tried it with a thread this way:
public void startProgress(View view, final String aWhatSearch) {
final ProgressDialog dialog = new ProgressDialog(MyActivity.this);
if (curDBCursor != null){
curDBCursor.close();
curDBCursor = null;
}
dialog.setMessage("Loading data...");
dialog.show();
Runnable runnable = new Runnable() {
public void run() {
curDBCursor = myDatabaseAdapter.fetchAll(aWhatSearch);
// dirty trick
try {
Thread.sleep(250); // it must be here to show progress
} catch (InterruptedException e) {
e.printStackTrace();
}
handler.post(new Runnable() {
public void run() {
if (dialog.isShowing()) {
dialog.dismiss();
}
prepareListView();
}
});
}
};
new Thread(runnable).start();
}
The result was the same, but when I used the trick with Thread.sleep(250);
ProgressDialog was shown during the database request. But it is not spinning,
it looks freezed during the DB request.
DB stuff is called this way (after tap on search button):
btnSearchAll.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
// AsyncTask
new GetDataFromLangDB().execute(edtTextToSearch.getText().toString());
// or Thread
//startProgress(null, edtTextToSearch.getText().toString());
}
});
I found a lot of problems like this in SO, but nothing was useful for me.
Could it be that DB is on SD Card?
I put the definition of the dialog into the AsyncTask Class and it works fine for me.
Take a look at this exampel (You have to change NAMEOFCLASS in the name of your CLASS:
private class doInBackground extends AsyncTask<Integer, Integer, Void> {
final ProgressDialog dialog = new ProgressDialog(NAMEOFCLASS.this) {
#Override
protected void onPreExecute() {
dialog.setCancelable(false);
dialog.setTitle(getString(R.string.daten_wait_titel));
dialog.setIcon(R.drawable.icon);
dialog.setMessage(getString(R.string.dse_dialog_speichern));
dialog.show();
}
#Override
protected void onCancelled() {
dialog.cancel();
}
....
#Override
protected void onProgressUpdate(Integer... values) {
// DO YOUR UPDATE HERE
}
#Override
protected void onPostExecute(Void result) {
dialog.dismiss();
}
}
Maybe this SO answer could help you. It looks like similar problem. Try to use AsyncQueryHandler for querying your database
declare you Dialog box on Class (Activity) level like this
private ProgressDialog dialog = null;
show the progress dialog and call the AsyncTask class when you want to start you Busy work..like onButton click or any
dialog = ProgressDialog.show(this,"Sending Email to your account please! wait...", true);
SendingEmailTask task = new SendingEmailTask();
String s = "";
task.execute(s);
create your inner class like
private class SendingEmailTask extends AsyncTask<String, Void, String> {
protected String doInBackground(String... urls) {
//do your work here..
// like fetching the Data from DB or any
return null;
}
#Override
protected void onPostExecute(String str) {
//hide progress dialog here
dialog.dismiss();
}
}
let me know if this help!!
I have spent many hours looking for a solution to this and need help.
I have a nested AsyncTask in my Android app Activity and I would like to allow the user to rotate his phone during it's processing without starting a new AsyncTask. I tried to use onRetainNonConfigurationInstance() and getLastNonConfigurationInstance().
I am able to retain the task; however after rotation it does not save the result from onPostExecute() to the outer class variable. Of course, I tried getters and setters. When I dump the variable in onPostExecute, that it is OK. But when I try to access to the variable from onClick listener then it is null.
Maybe the code will make the problem clear for you.
public class MainActivity extends BaseActivity {
private String possibleResults = null;
private Object task = null;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.task = getLastNonConfigurationInstance();
setContentView(R.layout.menu);
if ((savedInstanceState != null)
&& (savedInstanceState.containsKey("possibleResults"))) {
this.possibleResults = savedInstanceState
.getString("possibleResults");
}
if (this.possibleResults == null) {
if (this.task != null) {
if (this.task instanceof PossibleResultWebService) {
((PossibleResultWebService) this.task).attach();
}
} else {
this.task = new PossibleResultWebService();
((PossibleResultWebService) this.task).execute(this.matchToken);
}
}
Button button;
button = (Button) findViewById(R.id.menu_resultButton);
button.setOnClickListener(resultListener);
}
#Override
protected void onResume() {
super.onResume();
}
OnClickListener resultListener = new OnClickListener() {
#Override
public void onClick(View v) {
Spinner s = (Spinner) findViewById(R.id.menu_heatSpinner);
int heatNo = s.getSelectedItemPosition() + 1;
Intent myIntent = new Intent(MainActivity.this,
ResultActivity.class);
myIntent.putExtra("matchToken", MainActivity.this.matchToken);
myIntent.putExtra("heatNo", String.valueOf(heatNo));
myIntent.putExtra("possibleResults",
MainActivity.this.possibleResults);
MainActivity.this.startActivityForResult(myIntent, ADD_RESULT);
}
};
private class PossibleResultWebService extends AsyncTask<String, Integer, Integer> {
private ProgressDialog pd;
private InputStream is;
private boolean finished = false;
private String possibleResults = null;
public boolean isFinished() {
return finished;
}
public String getPossibleResults() {
return possibleResults;
}
#Override
protected Integer doInBackground(String... params) {
// quite long code
}
public void attach() {
if (this.finished == false) {
pd = ProgressDialog.show(MainActivity.this, "Please wait...",
"Loading data...", true, false);
}
}
public void detach() {
pd.dismiss();
}
#Override
protected void onPreExecute() {
pd = ProgressDialog.show(MainActivity.this, "Please wait...",
"Loading data...", true, false);
}
#Override
protected void onPostExecute(Integer result) {
possibleResults = convertStreamToString(is);
MainActivity.this.possibleResults = possibleResults;
pd.dismiss();
this.finished = true;
}
}
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (this.possibleResults != null) {
outState.putString("possibleResults", this.possibleResults);
}
}
#Override
public Object onRetainNonConfigurationInstance() {
if (this.task instanceof PossibleResultWebService) {
((PossibleResultWebService) this.task).detach();
}
return (this.task);
}
}
It is because you are creating the OnClickListener each time you instantiate the Activity (so each time you are getting a fresh, new, OuterClass.this reference), however you are saving the AsyncTask between Activity instantiations and keeping a reference to the first instantiated Activity in it by referencing OuterClass.this.
For an example of how to do this right, please see https://github.com/commonsguy/cw-android/tree/master/Rotation/RotationAsync/
You will see he has an attach() and detach() method in his RotationAwareTask to solve this problem.
To confirm that the OuterClass.this reference inside the AsyncTask will always point to the first instantiated Activity if you keep it between screen orientation changes (using onRetainNonConfigurationInstance) then you can use a static counter that gets incremented each time by the default constructor and keep an instance level variable that gets set to the count on each creation, then print that.
I'm a newbie to Android and I'm having some problems with the AsyncTask class. I have a lot of (over 100) check boxes which I want to select or deselect with a "select all" check box.
Unfortunately this takes quite some time, noticeable for the user, so I want to use the ProgressDialog to show that the selecting is in progress.
Now I know that updates of the UI elements are supposed to be done in the UI thread so I have the selecting code in the onProgressUpdate method but the ProgressDialog doesn't show up not until the processing (select or deselecting all checkboxes) is finished...
How is this supposed to be done? Is there any workaround or any other solution? I also tried to go through all the check boxes in the doInBackground() method but I get lots of weird errors (I guess it's because of the UI).
This is my code:
customPref.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
#Override
public boolean onPreferenceChange(final Preference preference, final Object object) {
new CbSelection().execute(preference);
return true;
}
});
private class CbSelection extends AsyncTask<Preference, Boolean, Void> {
private ProgressDialog Dialog = new ProgressDialog(Preferences.this);
#Override
protected void onPreExecute() {
Dialog.setMessage("Please wait...");
Dialog.setCanceledOnTouchOutside(true);
Dialog.setCancelable(true);
Dialog.show();
}
#Override
protected Void doInBackground(Preference... pref) {
PreferenceScreen screen = (PreferenceScreen)getPreferenceScreen().getRootAdapter().getItem(2);
Preference preference = pref[0];
if(preference.getKey().compareTo("select_all") == 0){
publishProgress(true);
}
return null;
}
#Override
protected void onProgressUpdate(Boolean... ok) {
if(ok[0]== true) {
PreferenceScreen screen = (PreferenceScreen)getPreferenceScreen().getRootAdapter().getItem(2);
for(int i = 0; i < screen.getPreferenceCount(); ++i) {
//Dialog.show();
/* select or deselect all checkboxes here... */
}
}
}
#Override
protected void onPostExecute(Void v) {
Dialog.cancel();
}
}
What if you create and show the dialog before executing the AsyncTask? That's the way it usually is used:
private ProgressDialog Dialog;
customPref.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
#Override
public boolean onPreferenceChange(final Preference preference, final Object object) {
Dialog = new ProgressDialog(Preferences.this);
Dialog.setMessage("Please wait...");
Dialog.setCanceledOnTouchOutside(true);
Dialog.setCancelable(true);
Dialog.show();
new CbSelection().execute(preference);
return true;
}
});
private class CbSelection extends AsyncTask<Preference, Boolean, Void> {
#Override
protected Void doInBackground(Preference... pref) {
PreferenceScreen screen = (PreferenceScreen)getPreferenceScreen().getRootAdapter().getItem(2);
Preference preference = pref[0];
if(preference.getKey().compareTo("select_all") == 0){
publishProgress(true);
}
return null;
}
#Override
protected void onProgressUpdate(Boolean... ok) {
if(ok[0]== true) {
PreferenceScreen screen = (PreferenceScreen)getPreferenceScreen().getRootAdapter().getItem(2);
for(int i = 0; i < screen.getPreferenceCount(); ++i) {
/* select or deselect all checkboxes here... */
}
}
}
#Override
protected void onPostExecute(Void v) {
Dialog.dismiss();
}
}