I am trying to cancel/end an async task on the button click of a pop-up box. However, when I click on the button the onCancelled() method is not being called. Could someone please help me with this? Below is my code:
public AuthenticationAsync(Context context, final String rid) {
this.rid = rid;
alertDialog = new AlertDialog.Builder(context).create();
alertDialog.setTitle("Information");
alertDialog.setMessage("RaspberryPi Validated");
alertDialog.setButton(AlertDialog.BUTTON_NEUTRAL, "OK",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
cancel(true); //Cancelling async task on button click
dialog.dismiss();
}
});
}
#Override
protected Object doInBackground(Object[] params) {
String metricData = String.valueOf(kuraPayload.getMetric("data"));
if (metricData.isEmpty())
subResponse = false;
}
#Override
protected void onCancelled() {
Log.d("Async","cancelled"); //not being called
}
#Override
protected void onPostExecute(Object o) {
if (subResponse) {
asyncHttpClient = new AsyncHttpClient();
asyncHttpClient.get(WSConstants.ADD_RPI_WS + MQTTFactory.getRaspberryPiById(), new AsyncHttpResponseHandler() {
#Override
public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
Toast.makeText(ActivityContexts.getMainActivityContext(), "RaspberryPi Validated", Toast.LENGTH_LONG).show();
alertDialog.show();
}
}
}
}
It looks like the it's never getting into onCancelled() because you are actually calling alertDialog.show() in onPostExecute(). Since doInBackground() has already completed, there is nothing to cancel.
The cancel() method is meant to be called during the execution of the doInBackground() method, which is the only method that runs on a background Thread.
Note that even if cancel() is called during execution of doInBackground(), the execution of the method won't be stopped immediately. You should do periodic checks to isCancelled() and if it returns true, then exit out of the method.
After doInBackground() has completed, if cancel() has been called, then onCancelled() will be called instead of onPostExecute().
Here is a very simple test based on your code:
public class AuthenticationAsync extends AsyncTask {
AlertDialog alertDialog;
String rid;
public AuthenticationAsync(Context context, final String rid) {
this.rid = rid;
alertDialog = new AlertDialog.Builder(context).create();
alertDialog.setTitle("Information");
alertDialog.setMessage("RaspberryPi Validated");
alertDialog.setButton(AlertDialog.BUTTON_NEUTRAL, "OK",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
cancel(true); //Cancelling async task on button click
dialog.dismiss();
}
});
}
#Override
protected void onPreExecute(){
//for testing, showing the dialog before the background Thread starts
alertDialog.show();
}
#Override
protected Object doInBackground(Object[] params) {
//cycle forever until the user clicks OK
while (true){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//Check if user clicked OK in the dialog
if (isCancelled()){
//Exit the method if the user dismissed the dialog
return null;
}
}
}
#Override
protected void onCancelled() {
Log.d("Async", "cancelled"); //this is called now
Toast.makeText(MainActivity.this, "cancelled", Toast.LENGTH_LONG).show();
}
#Override
protected void onPostExecute(Object o) {
Toast.makeText(MainActivity.this, "postExecute", Toast.LENGTH_LONG).show();
}
}
Result:
Dialog is shown during execution of doInBackground():
After clicking OK, onCancelled() is called:
#yanchenko answered something similar there -> Ideal way to cancel an executing AsyncTask
You can use one ProgressDialog and set this dialog as Cancelable and set the CancelListener.
Related
I am trying to stop an AsyncTask in Android. In doInBackground it runs through an IP - Address list and tries to ping all of them.
Furthermore, when executing the AsyncTask a ProgressDialog opens which you can cancel. Calling this cancel - text calls the onCancelled method in AsyncTask. So, this method is being called, but it seems as if it is never stopped because when I hit the button Find again, I always get the following error message:
java.lang.IllegalStateException: Cannot execute task: the task has already been executed (a task can be executed only once)
Here is my code:
private Settings.HostWorker hostWorker;
private void initComponents() {
hostWorker = new Settings.HostWorker();
progressDialog.setButton(DialogInterface.BUTTON_NEGATIVE, "Cancel", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
if(!hostWorker.isCancelled()) {
hostWorker.cancel(true);
}
}
});
}
findServerButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v)
{
if(findServerButton.isClickable()) {
progressDialog.show();
hostWorker.execute();
findServerButton.setClickable(false);
}
}
});
private class HostWorker extends AsyncTask<Integer, String, Void> {
#Override
protected Void doInBackground(Integer... params) {
try {
for (int i = 0; i <= 255; i++) {
String ip = createHostsList(i);
if (ip != null) {
publishProgress(ip);
}
if(isCancelled())
{
break;
}
}
} catch (Exception e) {
MyAlertDialog myAlertDialog = new MyAlertDialog(getBaseContext(), "Error in Settings - doInBackground: " + e.getMessage());
myAlertDialog.showAlertDialog();
}
return null;
}
protected void onProgressUpdate(String... values) {
hostsOnline.add(values[0]);
settingsCustomArrayAdapter.notifyDataSetChanged();
}
protected void onCancelled(){
findServerButton.setClickable(true);
}
#Override
protected void onPostExecute(Void aVoid)
{
super.onPostExecute(aVoid);
}
}
The error
Cannot execute task: the task has already been executed (a task can be executed only once)
suggests that you need to create a new HostWorker instance each time you want to run the task. You could achieve this by removing the line
hostWorker = new Settings.HostWorker();
from initComponents and instead adding it into your findServerButton OnClickListener like so:
findServerButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v)
{
if(findServerButton.isClickable()) {
progressDialog.show();
hostworker = new Settings.HostWorker(); // Line added here.
hostWorker.execute();
findServerButton.setClickable(false);
}
}
});
I have got a little problem while cancellling a AsyncTask, which is in a Fragment which processes some data. If a internet session is expired the AsyncTask should be cancelled and a dialog be shown to inform the user.
However if I cancel the AsyncTask the AlertDialog is not shown, I also noticed that onCancelled()is not being called but the onPostExecute()is still executed.
If someone pls could assist?
Edit: If I use the while method, the AlertDialog is shown but how do I properly cancel the AsyncTask as the code as of tryin doInBackground() still is being executed?
while (!isCancelled()) {
// Do stuff
}
Edit 2: Solved! It seemed the AsyncTask call was not correctly instinciated, below code works and now also onCancelled method is called and onPostExecute declined as it should be.
Cudos for Anudeep Bulla to point me in the right direction.
public class Tb3_Abonnement extends Fragment {
private AsyncTask<Void, Void, Void> task;
...
#Override // If Fragment is visible to user, start asynctask
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser) {
// execute AsyncTask
startFetch();
}
}
// Wrap (asynctask call) in a own method
public void startFetch() {
task = new FetchFacturen();
task.execute();
}
private class FetchFacturen extends AsyncTask<Void, Void, Void> {
// Create new AlertDialog
AlertDialog taskDialog = new AlertDialog.Builder(getActivity())
.setCancelable(false)
.setTitle("Mededeling Facturen:")
.setMessage("Uw sessie is verlopen! U dient zich weer opnieuw in te loggen. ")
.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
// Start LoginActivity
Intent mainIntent = new Intent(getActivity(), LoginActivity.class);
getActivity().startActivity(mainIntent);
getActivity().finish();
}
})
.setIcon(android.R.drawable.ic_dialog_info)
.create();
#Override
protected void onPreExecute() {
if (Helper.minutes <= 0) {
task.cancel(true);
} else {
showProgress(true);
}
}
#Override
protected Void doInBackground(Void... result) {
CharSequence cs1 = "€";
if (isCancelled()) {
util.disconnect();
return null;
}
try {
// The heavy stuff
}
}
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onCancelled() { // Correctly called
if(isCancelled()){
Log.e("CANCELLED", "TAB3");
this.taskDialog.show();
}
super.onCancelled();
}
#Override
protected void onPostExecute(Void result) { // Skipped
if(isCancelled()){
this.taskDialog.show();
} else {
setupInvoiceAdapter();
showProgress(false);
}
}
}
Here are the important code parts:
public class Tb3_Abonnement extends Fragment {
private FetchAbbo task;
...
#Override // If Fragment is visible to user, start asynctask
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser) {
// execute AsyncTask
task = new FetchAbbo();
task.execute();
}
public class FetchAbbo extends AsyncTask<Void, Void, Void> {
#Override
protected void onPreExecute() {
if (Helper.minutes <= 0) {
task.cancel(true); // Cancel AsyncTask
new AlertDialog.Builder(getActivity()) // Create new AlertDialog
.setCancelable(false)
.setTitle("Mededeling:")
.setMessage("Uw sessie is verlopen! U dient zich weer opnieuw in te loggen. ")
.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
// Start LoginActivity
}
})
.setIcon(android.R.drawable.ic_dialog_info)
.show();
}
}
#Override
protected Void doInBackground(Void... result) {
if (!this.isCancelled()) { // Executed
Log.e("FetchAbbo: ", "Cancelled!"); // Printed
util.disconnect();
return null;
}
try {
Log.e("FetchAbbo: ", "still running!");
// Do the heavy stuff
}
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onCancelled() {
Log.e("CANCELLED", "TAB3"); // Not printed, onCancelled() not called?
super.onCancelled();
}
#Override
protected void onPostExecute(Void result) {
setupTxtViews();
mScrollView.setVisibility(View.VISIBLE); // Set ScrollView visible
}
}
I may be wrong, but I don't seem to understand the logic of showing the dialog in onPreExecute everytime. Why not try this ?
public class FetchAbbo extends AsyncTask<Void, Void, Void> {
AlertDialog taskDialog = new AlertDialog.Builder(getActivity()) // Create new AlertDialog
.setCancelable(false)
.setTitle("Mededeling:")
.setMessage("Uw sessie is verlopen! U dient zich weer opnieuw in te loggen. ")
.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
// Start LoginActivity
}
})
.setIcon(android.R.drawable.ic_dialog_info)
.create();
#Override
protected void onPreExecute() {
if (Helper.minutes <= 0) {
task.cancel(true); // Cancel AsyncTask
}
}
#Override
protected Void doInBackground(Void... result) {
if (!this.isCancelled()) { // Executed
Log.e("FetchAbbo: ", "Cancelled!"); // Printed
util.disconnect();
return null;
}
try {
Log.e("FetchAbbo: ", "still running!");
// Do the heavy stuff
}
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onCancelled() {
Log.e("CANCELLED", "TAB3"); // Not printed, onCancelled() not called?
if(this.isCancelled()){
this.taskDialog.show();
}
super.onCancelled();
}
#Override
protected void onPostExecute(Void result) {
if(this.isCancelled()){
this.taskDialog.show();
} else {
setupTxtViews();
mScrollView.setVisibility(View.VISIBLE); // Set ScrollView visible
}
}
}
I currently trying to show a progress dialog on OnclickListener of a Dialogbox since my items are taking too long to fetch from Server.
I use Async task as suggested here (Android progress dialog) and this post (android problem with progress dialog) to show progress dialog The progress dialog is shown , however the code returns exception when it goes to do background that " Looper is not set". And when I set looper nothing happens.
I am not sure at this stage what is it that I am doing wrong.
public void firstMethod()
{
final CustomObj obj = getCustomObj();//not imp
Messages.getInstance().showAlert(MainActivity.this, "message", false, new DialogInterface.OnClickListener()
{
#Override
public void onClick(DialogInterface dialog, int which)
{
dialog.dismiss();
}
}, new DialogInterface.OnClickListener()
{
#Override
public void onClick(DialogInterface dialog, int which)
{
gotoAnotherPge(obj);
}
});
}
public void gotoAnotherPge(final CustomObject obj)
{
final ProgressDialog pd = new ProgressDialog(MainActivity.this);
new AsyncTask<Object, Object, Boolean>()
{
protected void onPreExecute()
{
pd.setMessage(String.format(Statics.getText(MainActivity.this, R.raw.dictionary, "subscriptions_loading_unsubscribing")));
pd.show();
}
protected Boolean doInBackground(Object... params)
{
try{
Looper.prepare();
final LocalPopulator lp = new LocalPopulator(MainActivity.this, 0)
{
#Override
public void populate()
{
List<Serializable> items = Arrays.asList(getItemHere(obj));
List<Serializable> listItems = new ArrayList<Serializable>();
listItems.addAll(items);
Serializable[] sItems = listItems.toArray(new Serializable[menuItems.size()]);
result = sItems;
}
};
showNextPage(true, 1, 0, lp);
Looper.loop();
}catch (Exception e){
Log.e("tag", e.getMessage());
/*
* The task failed
*/
return false;
}
return true;
}
protected void onPostExecute(Boolean result)
{
pd.dismiss();
}
};
MainActivity.this.runOnUiThread (new Runnable()
{
#Override
public void run()
{
// dismiss the progressdialog
pd.dismiss();
}
});
}
I know that there are many similar AsyncTask questions already, but in my case something is very unusual or am I missing something !?
As the AsyncTask is not allowed to run more than once. I call it with new B().execute(); so it should create a separated instance on each run !? Right?
The problem is after the B class is created and executed once, it wont work the second time the user calls the startBClass() method (just opens the dialog, but the actual work is not happening).
I just Debugged the code and realize that after the Dialog is closed, the Thread is still running in the background. What is the proper way to stop the background thread when the Dialog is closing? - And since I'm closing the first Dialog inside B class and create another instance of the B class, why is the second one not working? Can't multiple AsyncTasks run in parallel !?
I simplified the classes for easier understanding what I'm trying:
public class A {
/* Is called when user clicks a button */
private void startBClass() {
new B().execute();
}
/* Opens a Dialog with a countdown TextView (works first call only) */
private class B extends AsyncTask<Void, Integer, Void> {
private int secondsPassed = 0;
private double totalToPay = 0;
private Dialog dialog;
private TextView tvCost;
private Button dialogBtn;
#Override
protected void onPreExecute() {
super.onPreExecute();
dialog = new Dialog(ConfigurationActivity.this);
dialog.setCancelable(true);
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
dialog.setContentView(R.layout.dialog);
dialog.setCanceledOnTouchOutside(false);
dialog.setOnCancelListener(new OnCancelListener() {
#Override
public void onCancel(DialogInterface dialog) {
onPostExecute(null);
}
});
tvCost = (TextView) dialog.findViewById(R.id.textCounter);
dialogBtn = (Button) dialog.findViewById(R.id.button1);
dialogBtn.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
dialog.cancel();
}
});
dialog.show();
}
#Override
protected Void doInBackground(Void... arg0) {
while(true){
publishProgress(secondsPassed++);
SystemClock.sleep(1000);
}
}
#Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
totalToPay = 12.00;
tvCost.setText(totalToPay + " USD");
}
#Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
final AlertDialog alert = new AlertDialog.Builder(ConfigurationActivity.this).create();
alert.setTitle("Information");
alert.setMessage("You should pay about " + totalToPay + " USD.");
alert.setButton("OK", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface alertDialog, int which) {
alertDialog.dismiss();
}
});
alert.show();
}
}
}
dialog.setOnCancelListener(new OnCancelListener() {
#Override
public void onCancel(DialogInterface dialog) {
onPostExecute(null);
}
});
This is no good. Per the docs:
Do not call onPreExecute(), onPostExecute(Result), doInBackground(Params...), onProgressUpdate(Progress...) manually.
To end it, I'd change both the code above, and the while loop in doInBackground().
protected Void doInBackground(Void... arg0) {
while(running){
publishProgress(secondsPassed++);
SystemClock.sleep(1000);
}
}
running is a boolean you set to true in onPreExecute(). Set it to false when you want to end it. Then your loop will exit and onPostExecute() will be called correctly.
Side note: Where is secondsPassed ever used?
For the past few days, I haven't been able to solve an issue with my dialog box. I am running a thread to show the dialog box for 5000ms and removing it. and I am trying to show a toast("SUCCESS"). The problem is I am getting the dialog box for the second time also. I am new to android development and I need to solve this with Async Task so that I can delay the second thread and show a alert dialog.builder with a positive button instead of toast. I goggled a lot but I confused to to implement this
Here I am sending my credentials to server and while sending I am showing a progress dialog box for 5000ms and I want to have a separate thread in order to show the dialog.builder with a positive button.( When the user get a response in the logcat for that I should check the responsecode==1 or not from the logcat to show the builder)
plz someone help me to solve this with some code snippet if possible.
Thanks in advance.
this is the code where I need to implement Async task
Button signin = (Button) findViewById(R.id.regsubmitbtn);
signin.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
// showDialog(0);
t = new Thread() {
public void run() {
register();
try {
while (counter < 2) {
showmsg(0);
Thread.sleep(5000);
removeDialog(0);
// Toast.makeText(Register.this, "Registerd", Toast.LENGTH_LONG).show();
showmsg(1);
// toast.show();
Thread.sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
t.start();
}
});
}
#Override
protected Dialog onCreateDialog(int id) {
switch (id) {
case 0: {
++counter;
dialog = new ProgressDialog(this);
if (counter == 1) {
dialog.setMessage("Registering...");
}
else {
String resultsRequestSOAP = "";
if (Log.v("TAG", String.valueOf(resultsRequestSOAP)) == 1)
;
{
Context context = getApplicationContext();
CharSequence text = "Registerd";
int duration = Toast.LENGTH_LONG;
Toast toast = Toast.makeText(context, text, duration);
toast.show();
}
}
dialog.setIndeterminate(true);
dialog.setCancelable(true);
return dialog;
}
}
return null;
}
public void showmsg(int actionsToBePerformedOnScreen) {
Message msg = new Message();
msg.what = actionsToBePerformedOnScreen;
handler.sendMessage(msg);
}
public Handler handler = new Handler() {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 0:
showDialog(0);
break;
case 1:
// clear all images in the list
removeDialog(0);
break;
}
};
};
Easy: show dialog onPreExecute, register() in doInBackground and hide dialog in onPostExecute. Finally, do new RegisterTask().execute() in your onclick.
private class RegisterTask extends AsyncTask<Void, Void, Boolean> {
private final ProgressDialog dialog = new ProgressDialog(YourClass.this);
protected void onPreExecute() {
this.dialog.setMessage("Signing in...");
this.dialog.show();
}
protected Boolean doInBackground(final Void unused) {
return Main.this.register(); //don't interact with the ui!
}
protected void onPostExecute(final Boolean result) {
if (this.dialog.isShowing()) {
this.dialog.dismiss();
}
if (result.booleanValue()) {
//also show register success dialog
}
}
}
Why you don't use Android AsyncTask?
For example:
public class MyPreloader extends AsyncTask<InputObject, Void, OutputObject>{
private Context context;
private ProgressDialog dialog;
public MyPreloader(Context context){
this.context = context;
}
#Override
protected void onPreExecute() {
dialog = new ProgressDialog(context);
dialog.setMessage("Please wait...");
dialog.setIndeterminate(true);
dialog.show();
super.onPreExecute();
}
#Override
protected ResponseBase doInBackground(InputObject... params) {
InputObject input = params[0];
//some code for background work
}
#Override
protected void onPostExecute(OutputObject result) {
if (dialog.isShowing()) {
dialog.dismiss();
}
super.onPostExecute(result);
}