Asynctask doesn't stop after cancelling it in doInBackground - java

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.

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

Exit activity after killing all threads android

I am trying to play with progress bars. I have this (below) simple activity which runs a progress bar N times one after the other, when I call Progress(N). It is working great but the problem I am facing is, if I press back button. I get into the mainActivity but the progress bars (the threads) are still running in background one after the other. As soon as they finish N loops, the intent is called and whatever I would be doing would be interrupted by this LOOP_OVER activity.
I tried solving this by my own. I tried using variable of Thread class (before I was directly doing it). And tried to interrupt() it at onDestroy() or even just before the intent is called but its not helping. How should I go about it?
public class Loop extends Activity {
private ProgressBar progressBar;
private CircleProgress circleProgress;
private int progressStatus = 0;
private Handler handler = new Handler();
private TextView myView;
private int started = 0, doneLoop=0;
private Thread th;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_loop);
progressBar = (ProgressBar) findViewById(R.id.progressBar1);
circleProgress = (CircleProgress) findViewById(R.id.circle_progress);
myView = (TextView) findViewById(R.id.instruction);
progressBar.setScaleY(3f);
// Start long running operation in a background thread
Progress(3);
}
#Override
public void onDestroy() {
// Below, everything I am just
th.interrupt();
Loop.this.finish();
Thread.currentThread().interrupt();
super.onDestroy();
}
public void Progress(final int numberOfRuns){
// QueView.setText(Que);
if(numberOfRuns == 0){
th.interrupt();
Intent myIntent = new Intent(Loop.this, LOOP_OVER.class);
startActivity(myIntent);
super.onDestroy();
finish();
}
th = new Thread(new Runnable() {
public void run() {
genNextSet();
while (progressStatus < 100) {
progressStatus += 1;
// Update the progress bar and display the
//current value in the text view
handler.post(new Runnable() {
public void run() {
circleProgress.setProgress(progressStatus);
progressBar.setProgress(progressStatus);
textView.setText(progressStatus+"/"+progressBar.getMax());
}
});
try {
runOnUiThread(new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
myView.setText(Que);
}
});
// Sleep for 200 milliseconds.
//Just to display the progress slowly
Thread.sleep(30);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
progressStatus = 0;
Progress(numberOfRuns - 1);
}
});
th.start();
}
private void genNextSet() {
// so some cool here!
}
}
You can think of a class variable that is shared among all threads.
Try to add something like this:
private Boolean LOOP = true;
then
while (progressStatus < 100 && LOOP) {
and
#Override
public void onBackPressed() {
LOOP = false
}
also
if(LOOP == true){
// call intent
}
finish();
Your activity does not get destroyed, if you press the "Back"-key, thus onDestroy() will not be called.I'd override onBackPressed(), if I where you.Alternatively, you could try to put it into the onPause()-method.
You haven't override the back button pressed..try this
#Override
public void onBackPressed() {
th.interrupt();
Loop.this.finish();
Thread.currentThread().interrupt();
super.onBackPressed();
// add finish() if you want to kill current activity
}

CountDown Method for Android not Working [Edited]

I am using Android Studio. So this is the full code. I am trying to start the countdown by clicking the startButton. The plan is that the countdown is going down while I can still click the other button. The other button(incrementButton) increments. I got the increment working but the timer is not working when I click the startButton. Is there something wrong I am doing?
EDIT : Would it also be good to ask on how to cancel the background thread?
I tried using cancel(true). Changed the code
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
TextView seconds;
TextView increment;
int count;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
seconds = (TextView) findViewById(R.id.seconds);
increment = (TextView) findViewById(R.id.increment);
Button incrementButton = (Button) findViewById(R.id.incrementButton);
Button startButton = (Button) findViewById(R.id.startButton);
Button stopButton= (Button) findViewById(R.id.stopButton);
incrementButton.setOnClickListener(this);
startButton.setOnClickListener(this);
}
#Override
public void onClick(View view) {
switch(view.getId()){
case R.id.incrementButton:
count++;
increment.setText(Integer.toString(count));
break;
case R.id.startButton:
new timerTask();
break;
case R.id.stopButton:
new timerTask().cancel(true);
break;
}
}
private void updateSeconds(final int count) {
runOnUiThread(new Runnable() {
#Override
public void run() {
seconds.setText(Integer.toString(count));
}
});
}
public class timerTask extends AsyncTask<Void, Void, Void> {
private int i;
private int savedSecond;
public timerTask() {
}
#Override
protected Void doInBackground(Void... voids) {
try {
for(i = 10; i > 0; i--){
savedSecond = i;
updateSeconds(savedSecond);
Thread.sleep(1000); //1000 = 1 seconds
if(isCancelled())
break;
}
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}
}
}
Call execute method to start AsyncTask as:
case R.id.startButton:
new timerTask().execute();
break;
case R.id.startButton:
timerTask timerTask =new timerTask(); // here you created the timer task.
timerTask.execute() //execute has to be called.
break;

AsyncTask not cancelling upon cancel(boolean)

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(..).

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