AsyncTask not cancelling upon cancel(boolean) - java

I am having some issues cancelling an AsyncTask. I am fairly new to Android programming, so I've probably made a typo somewhere.
What happens when i press the cancel button is as follows:
Invoking cancel(true) within the cancelButton's onClick event appears to modify the variable used by isCancelled(), so that the for-loopin doInBackground will be terminated by the break statement. However:
onCancelled(String) is not invoked.
I get expected behavior if I don't press cancel, so the question only concerns the onCancelled method.
The code is as follows:
public class CustomAsyncTask extends AsyncTask<String, Integer, String> {
//Variables to be used by onPreExecute, onPostExecute, onProgressUpdate
//and onCancelled, as they can operate on the UI thread.
private ProgressBar progressBar;
private Button submitButton;
private Button cancelButton;
private TextView resultText;
private int max;
//Constructor initializing the field variables.
#Override
protected void onPreExecute() {
progressBar.setProgress(0);
progressBar.setVisibility(View.VISIBLE);
submitButton.setVisibility(View.INVISIBLE);
cancelButton.setVisibility(View.VISIBLE);
resultText.setBackgroundResource(R.color.white);
resultText.setText("Working on item 1 of " + max);
cancelButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
cancel(true);
}
});
}
#Override
protected String doInBackground(String ... strings) {
for(int count = 0; count < strings.length; ++count) {
if(isCancelled()) {
break;
}
try {
Thread.sleep(1000);
publishProgress(count);
} catch(Exception e) {
throw new Error(e);
}
}
return "Task completed: " + strings[0];
}
#Override
protected void onPostExecute(String result) {
progressBar.setVisibility(View.INVISIBLE);
submitButton.setVisibility(View.VISIBLE);
cancelButton.setVisibility(View.INVISIBLE);
resultText.setText(result);
}
#Override
protected void onProgressUpdate(Integer ... values) {
progressBar.setProgress(values[0]);
//don't mind the line under, some testing...
resultText.setText("Working on item " + (values[0]+2) + " of " + max);
}
#Override
protected void onCancelled(String result) {
progressBar.setVisibility(View.INVISIBLE);
submitButton.setVisibility(View.VISIBLE);
cancelButton.setVisibility(View.INVISIBLE);
resultText.setBackgroundResource(R.color.red);
resultText.setText("Canceled.");
}
}
Does anyone have a clue why onCancelled(String) is not invoked? I've been looking for similar questions on StackOwerFlow, but haven't found any that answer my problem :-)
The AsyncTask is started by the following Activity (upon pressing the asyncTaskSubmit button):
public class AsyncTaskActivity extends AppCompatActivity {
private EditText editText;
private Button asyncTaskSubmit;
private Button asyncTaskCancel;
private ProgressBar progressBar;
private TextView textView;
private static final int MAX = 10;
#Override
protected void onCreate(Bundle savedInstanceState) {
//call super, initialize fieldvariables with findViewById.
asyncTaskSubmit.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String text = editText.getText().toString();
String [] doJobs = new String[MAX];
//dummydata
for(int x = 0; x < doJobs.length; x++) {
doJobs[x] = text;
}
new CustomAsyncTask(
progressBar,
asyncTaskSubmit,
asyncTaskCancel,
textView,
MAX
).execute(doJobs);
}
});
}
}
Additional information:
SDK (min): 16
SDK (target): 23
Emulator device: Google nexus 5 (API 23)
Thanks in advance!
-Superdids

A tiny modification seems to provide the expected results. I changed the argument of cancel(boolean) from true to false:
cancelButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//cancel(true);
cancel(false);
}
});
As the argument (boolean mayInterruptIfRunning) states:
True if the thread executing this task should be interrupted; otherwise, in-progress tasks are allowed to complete.
At least as of my implementation, this implies that the doInBackground method won't return if I pass true as argument in cancel(..).

Related

Why value of only last iteration(999) is printed in the text field ? I want all numbers to be printed before getting replaced by next number

I am new to android studio and want to overcome from this problem.
It gonna help me a lot while creating some future apps like , stopwatch, timer etc etc.
Thanks in advance !!
public class MainActivity extends AppCompatActivity {
private EditText k;
private Button start;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
k=findViewById(R.id.kf);
start=findViewById(R.id.startf);
start.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
for(int i=1;i<1000;i++)
{
k.setText(String.valueOf(i) );
}
}
});
}
}
As #f1sh already mentioned in the comments your for loop is executing at such a speed that all you see is the final value. For such cases in android one of the best solutions is to make use of Handler for posting delayed functions without blocking the UI.
So for showing 1 to 999 you can try something like this:
public class MainActivity extends AppCompatActivity {
private EditText k;
private Button start;
int count;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
k=findViewById(R.id.kf);
start=findViewById(R.id.startf);
final Handler handler = new Handler();
final Runnable runnable = new Runnable() {
public void run() {
if (count < 1000) {
k.setText(String.valueOf(count));
count++;
handler.postDelayed(this, 1000);
} else {
handler.removeCallbacks(this);
}
}
};
start.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
count = 1;
handler.postDelayed(runnable, 0);
}
});
}
}
this will keep changing the text in TextView with a 1 second delay, you can change the delay as needed by setting the milliseconds in runnable.
You can even use a countdown timer for this purpose but its more like a workaround and requires you to calculate the correct time etc.
For example displaying 1 to 10 would be something like this:
...
count = 1;
new CountDownTimer(11000, 1000) {
public void onTick(long millisUntilFinished) {
tv.setText(String.valueOf(count));
count++;
}
public void onFinish() {
}
}.start();
here you are displaying the value every 1 second for 11 seconds

Asynctask doesn't stop after cancelling it in doInBackground

I'm trying to code a progress bar, that increases depending on a timer (starts at 0 and goes up to a certain time I choose).
When I click on a button, the progress bar decreases (decreases from values[0] - 5).
When the progress bar value is 30, I want to stop the progressbar, so stop asynctask, and restart it so the progress bar value is now 0.
My code doesn't have any error, the problem is that the progressbar doesn't stop and doesn't restart. (I know that because I made a TOAST to print the value of the progress bar, and it never stops)
This is my code in MainActivity
public class MainActivity extends Activity {
ProgressBar progressBar_eau;
TextView txt;
int compteur = 0;
Button btn_arrosoir;
MyTask task_eau;
Boolean restart = true;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn_arrosoir = (Button) findViewById(R.id.btn_arrosoir);//my button when you click the progress bar decreases
progressBar_eau = (ProgressBar) findViewById(R.id.barre_eau);
progressBar_eau.setMax(30);
Drawable draw3=getResources().getDrawable(R.drawable.custom_eau);
progressBar_eau.setProgressDrawable(draw3);
progressBar_eau.setProgress(0);
task_eau = new MyTask(progressBar_eau);
task_eau.execute(300);
btn_arrosoir.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//OnCLick Stuff
compteur = compteur - 5;
}
});
}
public void cancelAsynctask(){
if (restart == false){
task_eau.cancel(true);
restartAsynctask();
Toast.makeText(MainActivity.this,
"cancel", Toast.LENGTH_LONG).show();
restart = true;
}
}
public void restartAsynctask(){
task_eau = new MyTask(progressBar_lumiere);
task_eau.execute(300);
Toast.makeText(MainActivity.this,
"restart", Toast.LENGTH_LONG).show();//my emulator shows both message start and restart even though the progress bar doesn't stop
}
public class MyTask extends AsyncTask<Integer, Integer, String> {
ProgressBar progressBar_actuelle;
public MyTask(ProgressBar target) {
progressBar_actuelle = target;
}
#Override
protected String doInBackground(Integer... params) {
while (task_eau.isCancelled() == false){
for (; count <= params[0]; count++) {
try {
Thread.sleep(1000);
publishProgress(count);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
return null;
}
#Override
protected void onPostExecute(String result) {
}
#Override
protected void onProgressUpdate(Integer... values) {
progressBar_actuelle.setProgress(values[0]+compteur);
if (progressBar_actuelle.getProgress() >= 30){
restart = false;
cancelAsynctask();
}
}
}
}
I think the problem is with the Progressbar declared in AsyncTask.Remove that and directly use the progressbar object created in onCreate method.I also observed that you are not dismissing progressbar at any point in the code. Look into it as well.

AsyncTask.execute() doesn't wait for doInBackground to complete

I know this is a duplicate question but please hold on. I have read some similar questions and answer but none of them seems working for me.
What to do:
I have to do a search which will send a request to a web service and receive a response.
As i can't consume network on UI thread, I used AsyncTask.
What i tried:
I tried using task.execute() this returns immediately without even showing progressdialog box and i receive response as null (set in onPostExecute)
if i use task.execute.get() then it freezes screen and again no dialog box shows up (but i receive response correctly).
Below is my code with task.execute. Kindly correct me.
public class LookIn extends AppCompatActivity implements View.OnClickListener{
private Button btn=null;
private TextView txtPinCode=null;
private Service service=null;
private final static int timeout=20;
private String jsonResponse;
//private ProgressBar helperSearchProgressBar;
private String pincode="";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_look_in);
btn=(Button)findViewById(R.id.button);
btn.setOnClickListener(this);
txtPinCode=(TextView) findViewById(R.id.txtPinCode);
this.service=(Service) ParamFactory.getParam(ConstantLabels.SELECTED_SERVICE_ID);
// this.helperSearchProgressBar=(ProgressBar)findViewById(R.id.helperSearchProgressBar);
}
#Override
public void onClick(View v) {
String pincode= txtPinCode.getText().toString();
if(pincode==null || pincode.isEmpty() || pincode.length()!=6)
{
this.txtPinCode.setError("Please enter a 6 degit pin code from 700000 to 700200");
return;
}
ParamFactory.setParam(ConstantLabels.PINCODE_ID,pincode);
this.pincode=pincode;
loadHelper();
Intent intent= new Intent(LookIn.this,SearchResult.class);
startActivity(intent);
}
public void setJsonResponse(String jsonResponse)
{
this.jsonResponse=jsonResponse;
}
private void loadHelper()
{
Log.v("Callme", "Running thread:" + Thread.currentThread().getId());
ArrayAdapter<User> adapter=null;
String params=this.pincode+","+this.service.getId();
List<User> result=null;
try {
new CallmeGetHelperAsyncTask().execute(params); //my task.execute()
result= RestUtil.getUserList(jsonResponse);
adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, result);
ParamFactory.setParam("getHelperForService", adapter);
}
catch(JSONException x)
{
Log.e("Callme", Log.getStackTraceString(x));
}
}
class CallmeGetHelperAsyncTask extends AsyncTask<String,Void,String > {
// private Context context=null;
private ProgressDialog dialog=null;
private String jsonResponse;
private LookIn activity;
public CallmeGetHelperAsyncTask(){}
public CallmeGetHelperAsyncTask(LookIn activity)
{
this.activity=activity;
}
#Override
protected void onPreExecute() {
this.dialog= new ProgressDialog(LookIn.this);
this.dialog.setMessage("Loading...");
this.dialog.show();
Log.v("Callme","Dialog Shown");
}
#Override
protected void onPostExecute(String s) {
if(s!=null)
{
this.activity.setJsonResponse(s);
}
else
{
Log.v("Callme","kill me");
}
if(this.dialog.isShowing())
{
Log.v("Callme","Closing Dialog");
this.dialog.dismiss();
}
}
#Override
protected String doInBackground(String... params) {
Log.v("Callme","From Background:"+Thread.currentThread().getId());
String pincode=params.clone()[0].split(",")[0];
String serviceId=params.clone()[0].split(",")[1];
String url=String.format(URL.GET_HELPER,serviceId,pincode);
jsonResponse= null;
try {
jsonResponse = RestUtil.makeRestRequest(url);
} catch (IOException e) {
e.printStackTrace();
}
return jsonResponse;
}
}
}
Note: I haven't tried using while loop to waiting for the asynctask, because i think that will also end up freezing my screen. Please correct me if i am wrong
I haven't tried using while loop to waiting for the asynctask
No need to use loop for waiting AsyncTask Result.
Because onPostExecute method execute after doInBackground so instead of using jsonResponse just after call of execute method, do it inside setJsonResponse method, because this method called from onPostExecute which always run on Main UI Thread:
public void setJsonResponse(String jsonResponse)
{
this.jsonResponse=jsonResponse;
//Create adapter object here
result= RestUtil.getUserList(jsonResponse);
adapter = new ArrayAdapter(...);
ParamFactory.setParam("getHelperForService", adapter);
}

The final local variable checkstate cannot be assigned, since it is defined in an enclosing type

I have a class like this:
public class TimerActivity extends Activity
{
CountDownTimer cntTimer = null;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_timers);
final ImageButton startTimerButton = (ImageButton)findViewById(R.id.timer1ImageButton);
final EditText timerText = (EditText) findViewById(R.id.timerEditText1);
final TextView timerTextValue = (TextView) findViewById(R.id.timerTextView);
boolean checkstate =false;
timerCountDown(checkstate,startTimer3Button,timerText3, timerTextValue3);
}
public void timerCountDown(boolean check,final ImageButton startTimerImageButton ,
final EditText timerText,final TextView timerTextValue)
{
Integer input = 0;
if(timerText.getText().toString()!="")
{
input = Integer.parseInt(timerText.getText().toString())*1000 ;
}
CountDownTimer timer = new CountDownTimer(input, 1000)
{
public void onTick(long millisUntilFinished)
{
timerTextValue.setText("seconds remaining: " + millisUntilFinished / 1000);
}
public void onFinish()
{
timerTextValue.setText("done!");
}
};
timerStatus(check,startTimerImageButton,timer);
}
public void timerStatus(final boolean checkstate, final ImageButton startTimer3Button ,final CountDownTimer downTimer)
{
startTimer3Button.setOnClickListener(new View.OnClickListener()
{
public void onClick(View v)
{
if(checkstate==false)
{
startTimer3Button.setImageResource(R.drawable.reset);
//Error
checkstate = true;
downTimer.start();
}
else
{
startTimer3Button.setImageResource(R.drawable.start);
//Error
checkstate = false;
downTimer.cancel();
}
}
});
}
}
but I get this error on the timerStatue method for checkstate = false and checkstate = true:
The final local variable checkstate cannot be assigned, since it is defined in an enclosing type!
I searched google and stackoverflow but did not find compatible answer for my problem!
Can you help me?
thanks in advance!
I don't know want you meant to do with your code. It's a little fuzzy. As the Yazan said it's better to implement OnClickListener interface to avoid sending final to it's method.
After that why everything is final? You must define your variables in class level (class variables) to access them from other methods easily and the onCreate() method set their primary values.
So whatever I fixed your code like below without any error but I still didn't what It meant to do :
public class TimerActivity extends Activity
{
CountDownTimer cntTimer = null;
ImageButton startTimerButton;
EditText timerText;
TextView timerTextValue;
boolean checkstate =false;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_timers);
startTimerButton = (ImageButton)findViewById(R.id.timer1ImageButton);
timerText = (EditText) findViewById(R.id.timerEditText1);
timerTextValue = (TextView) findViewById(R.id.timerTextView);
timerCountDown();
}
public void timerCountDown()
{
Integer input = 0;
if(timerText.getText().toString()!="")
{
input = Integer.parseInt(timerText.getText().toString())*1000 ;
}
CountDownTimer timer = new CountDownTimer(input, 1000)
{
public void onTick(long millisUntilFinished)
{
timerTextValue.setText("seconds remaining: " + millisUntilFinished / 1000);
}
public void onFinish()
{
timerTextValue.setText("done!");
}
};
timerStatus(timer);
}
public void timerStatus(final CountDownTimer downTimer)
{
startTimerButton.setOnClickListener(new View.OnClickListener()
{
public void onClick(View v)
{
if(checkstate==false)
{
startTimerButton.setImageResource(android.R.drawable.ic_secure);
//Error
checkstate = true;
downTimer.start();
}
else
{
startTimerButton.setImageResource(android.R.drawable.ic_delete);
//Error
checkstate = false;
downTimer.cancel();
}
}
});
}
}
you can make a class that implements OnClickListener or even your current class can do that, and override onclick() in that way you can access any variable (not only final)
example: Edit
1) make you activity implements OnClickListener
2) override the method onClick() in your activity.
3) move boolean checkstate =false; at class level (outside of onCreate())
4) change startTimer3Button.setOnClickListener(... to startTimer3Button.setOnClickListener(TimerActivity.this);
6) move the code inside onClick() that exist in timerStatus() to the onclick() added to the activity class
public class TimerActivity extends Activity implements OnClickListener{
//code and methods ....
public void timerStatus(final boolean checkstate, final ImageButton startTimer3Button ,final CountDownTimer downTimer)
{
startTimer3Button.setOnClickListener(TimerActivity.this);
}
//code and methods ....
#override
public void onClick(View v){
if(checkstate==false)
{
startTimer3Button.setImageResource(R.drawable.reset);
//Error
checkstate = true;
downTimer.start();
}
else
{
startTimer3Button.setImageResource(R.drawable.start);
//Error
checkstate = false;
downTimer.cancel();
}
}
}

Android Splash Screen AsyncTask

Basically I have a loading splash screen which will be executed when button was clicked:
public void onClick(View v) {
// Load the loading splash screen
Intent loadingIntent = new Intent(context, LoadingScreen.class);
context.startActivity(loadingIntent);
}
});
And in the LoadingScreen class:
public class LoadingScreen extends Activity{
//A ProgressDialog object
private ProgressDialog progressDialog;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
//Initialize a LoadViewTask object and call the execute() method
new LoadViewTask().execute();
}
//To use the AsyncTask, it must be subclassed
private class LoadViewTask extends AsyncTask<Void, Integer, Void>
{
//Before running code in separate thread
#Override
protected void onPreExecute()
{
progressDialog = ProgressDialog.show(LoadingScreen.this,"Getting routes...",
"Loading data, please wait...", false, false);
}
//The code to be executed in a background thread.
#Override
protected Void doInBackground(Void... params)
{
try
{
//Get the current thread's token
synchronized (this)
{
//Initialize an integer (that will act as a counter) to zero
int counter = 0;
//While the counter is smaller than four
while(counter <= 4)
{
//Wait 850 milliseconds
this.wait(750);
//Increment the counter
counter++;
//Set the current progress.
//This value is going to be passed to the onProgressUpdate() method.
publishProgress(counter*25);
}
}
}
catch (InterruptedException e)
{
e.printStackTrace();
}
return null;
}
//Update the progress
#Override
protected void onProgressUpdate(Integer... values)
{
//set the current progress of the progress dialog
progressDialog.setProgress(values[0]);
}
//after executing the code in the thread
#Override
protected void onPostExecute(Void result)
{
finish();
//close the progress dialog
progressDialog.dismiss();
}
}
}
With these codes, the loading splash screen did came out. But I wonder is there any other way to show only the pop out dialogue for loading progress bar which on top on my previous screen?
Let's say my previous screen was event details. Then when user selected the button, only the dialogue box with loading progress bar will be shown instead of a new intent with a dialogue box.
Any ideas? Thanks in advance.
EDIT
public void onClick(View v) {
// Load the loading splash screen
new LoadViewTask().execute();
ENeighbourhoodActivity.tvDirection.setText("");
eventModel.setEventX(String.valueOf(eventModel.getEventX()));
eventModel.setEventY(String.valueOf(eventModel.getEventY()));
new GetEventDirectionAsyncTask(new GetEventDirectionAsyncTask.OnRoutineFinished() {
public void onFinish() {
//Hide the callout and plot user location marker
ENeighbourhoodActivity.callout.hide();
EventController.getUserLocation(context);
getActivity().finish();
}
}).execute(eventModel);
}
});
public class GetRegisteredEventAsyncTask extends
AsyncTask<String, Integer, Double> {
static EventController eventCtrl = new EventController();
public static ArrayList<Event> upcomingModel = new ArrayList<Event>();
public static ArrayList<Event> pastModel = new ArrayList<Event>();
public interface OnRoutineFinished { // interface
void onFinish();
}
private OnRoutineFinished mCallbacks;
public GetRegisteredEventAsyncTask(OnRoutineFinished callback) {
mCallbacks = callback;
}
public GetRegisteredEventAsyncTask() {
} // empty constructor to maintain compatibility
#Override
protected Double doInBackground(String... params) {
try {
upcomingModel = eventCtrl.getRegisteredUpcomingEvent(params[0]);
pastModel = eventCtrl.getRegisteredPastEvent(params[0]);
} catch (JSONException e) {
e.printStackTrace();
}
return null;
}
protected void onPostExecute(Double result) {
if (mCallbacks != null)
mCallbacks.onFinish(); // call interface on finish
}
protected void onProgressUpdate(Integer... progress) {
}
}
In your onClick() method you could write something like:
new LoadViewTask().execute();
and the progress dialog will be shown in that page itself.
what are you doing man, just call your AsyncTask not the intent
public void onClick(View v)
{
new LoadViewTask().execute();
}
});
do your intent in postExecute
#Override
protected void onPostExecute(Void result)
{
finish();
//close the progress dialog
progressDialog.dismiss();
//START YOUR ACTIVITY HERE
Intent loadingIntent = new Intent(context, LoadingScreen.class);
context.startActivity(loadingIntent);
}
Must read the documentation of AsynTask

Categories

Resources