Android SDK v15 running on a 2.3.6 device.
I'm having an issue where onPostExecute() is still being called when I am calling cancel() within a doInBackground() call.
Here is my code:
#Override
public String doInBackground(String... params) {
try {
return someMethod();
} catch (Exception e) {
cancel(true);
}
return null;
}
public String someMethod() throws Exception {
...
}
I am forcing someMethod() to throw an exception to test this out, and instead of onCancelled being called, I always return to onPostExecute(). If I check isCancelled() the returned value is true, so I know that cancel(true) is being executed.
Any ideas?
According to the Android API document, onCancelled() is there since API level 3, while onCancelled(Object result) had been added only since API level 11. Because of that, if the platform API level is below 11, onCancelled() will be invoked always while onCancelled(Object) will be invoked always otherwise.
So, if you want to run your code on all API level 3 and above, you need to implement both methods. In order to have the same behavior you may want to store the result in an instance variable so that isCancelled() can be used as shown below:
public class MyTask extends AsyncTask<String, String, Boolean> {
private Boolean result;
// . . .
#Override
protected void onCancelled() {
handleOnCancelled(this.result);
}
#Override
protected void onCancelled(Boolean result) {
handleOnCancelled(result);
}
//Both the functions will call this function
private void handleOnCancelled(Boolean result) {
// actual code here
}
}
By the way, Eric's code does not likely work because the Android API doc says:
Calling the cancel() method will result in onCancelled(Object) being invoked on the UI thread after doInBackground(Object[])
returns. Calling the cancel() method guarantees that
onPostExecute(Object) is never invoked.
onCancelled is only supported since Android API level 11 (Honeycomb 3.0.x). This means, on an Android 2.3.6 device, it will not be called.
Your best bet is to have this in onPostExecute:
protected void onPostExecute(...) {
if (isCancelled() && Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
onCancelled();
} else {
// Your normal onPostExecute code
}
}
If you want to avoid the version check, you can instead do:
protected void onPostExecute(...) {
if (isCancelled()) {
customCancelMethod();
} else {
// Your normal onPostExecute code
}
}
protected void onCancelled() {
customCancelMethod();
}
protected void customCancelMethod() {
// Your cancel code
}
Hope that helps! :)
Related
My app uses an AsyncTask to download files while displaying a ProgressDialog (I'm aware that it's deprecated) with a "Cancel" button.
According to this you should check isCancelled() in doInBackground periodically because mytask.cancel(true) won't interrupt doInBackground on its own.
I simply cancelled the task without checking at first and noticed that it still stops doInBackground: Depending on how long I let it download before pressing the "Cancel" button, I've seen different sizes in the resulting file - from just a few kb to a couple of mb - the final size would have been around 9mb.
How is this possible? Do you actually not have to call isCancelled() anymore?
My AsyncTask:
private class DownloadTask extends AsyncTask<String, String, String> {
protected void onPreExecute() {
progressdialog.setMessage("Preparing Download...");
progressdialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
progressdialog.setProgressNumberFormat(null);
progressdialog.setProgressPercentFormat(null);
progressdialog.setIndeterminate(true);
progressdialog.setButton(DialogInterface.BUTTON_NEGATIVE, "Cancel", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
progressdialog.dismiss();
mytask.cancel(true);
}
});
progressdialog.show();
}
protected String doInBackground(String... bla) {
String error = download();
return error;
}
protected void onProgressUpdate(String... s) {
//....
}
protected void onPostExecute(String s) {
progressdialog.dismiss();
//....
}
According to this you should check isCancelled() in doInBackground
periodically because mytask.cancel(true) won't interrupt
doInBackground on its own.
Actually it is not true.
According to documentation:
After invoking this method, you should check the value returned by
isCancelled() periodically from doInBackground(Object[]) to finish the
task as early as possible.
It means you can additionally check for isCancelled() to stop AsyncTask earlier if it is started.
mytask.cancel(true) will stop execution anyway.
Let`s see under the hood what is going on
When you call mytask.cancel(true):
public final boolean cancel(boolean mayInterruptIfRunning) {
mCancelled.set(true);
return mFuture.cancel(mayInterruptIfRunning);
}
Where mFuture is FutureTask that holds runnable inside
Then mFuture.cancel is called:
public boolean cancel(boolean mayInterruptIfRunning) {
if (state != NEW)
return false;
if (mayInterruptIfRunning) {
if (!UNSAFE.compareAndSwapInt(this, stateOffset, NEW, INTERRUPTING))
return false;
Thread t = runner;
if (t != null)
t.interrupt();
UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED); // final state
}
else if (!UNSAFE.compareAndSwapInt(this, stateOffset, NEW, CANCELLED))
return false;
finishCompletion();
return true;
}
Where runner is just
private volatile Thread runner;
Since its just thread, lets see what interrupt does in your case:
If this thread is blocked in an I/O operation upon an interruptible
channel then the channel will be closed, the thread's interrupt status
will be set, and the thread will receive a ClosedByInterruptException.
So if your download() method uses InterruptibleChannel interrupt will work.
In other words looks like you have never had to call isCancelled() to interrupt AsyncTask =) since Thread.interrupt can stop io blocking operation in your case.
I need to call task() function from doInBackground() of AsyncTask class. The task function contains 2 sub-async task. So the task() return immediately from doInBackground().
Is it possible to stop(or mark this task done) this task from anywhere else?
How to wrap two sub async task in one.
You don't need to call another AsyncTask from within doInBackground. As a matter of fact, once you get to a high enough API, you'll get an exception. You can call another long running method from within AsyncTask without needing to worry about a thread; you're already in a background thread. If you really feed the need, call another service, but there is no reason to do this.
To stop an AsyncTask, just override OnCancelled. Then you can just call:
task.cancel(true).
EDIT:
If you want to wait for another process to finish before you move on, you can wait for that process to finish by setting a global variable in your class or application and then doing a Thread sleep until done. You will not get an ANR because you are already in a background thread and not on the Main UI:
private boolean processIsDone = false.
//then in your method you are calling from AsyncTask:
private void myLongRunningMethod() {
//do your work here....
//at the end set
processIsDone = true;
}
//in your AsyncTask:
protected Void doInBackground(Void... params) {
//do first part of AsyncTask here
myLongRunningMethod();
do {
try {
Thread.sleep(1500);
} catch (InterruptedException e) {
e.printStackTrace();
}
} while (!processIsDone);
//finish the process here.
return null;
}
I do not understand the question exactly but maybe this can help. Use this class in your Activity like this:
myTask = new BackgroundAsyncTask().execute();
And cancel this way:
myTask.cancel(true);
This is the code of the class:
private class BackgroundAsyncTask extends AsyncTask<Object , Object ,String> {
protected void onPreExecute(){
// Do Before execute the main task
}
protected String doInBackground(Object... param) {
//Execute the main task and return for example and String
return res;
}
protected void onPostExecute(String result) {
// You can use the String returned by the method doInBackground and process it
}
}
Hope it's help
About your first question, you can catch event when your task is cancelled by onCancelled() method. try like this:
private CancelTask extends AsyncTask {
private boolean cancelled = false;
protected void onCancelled() {
cancelled = true;
}
protected Object doInBackground(Object... obj) {
do {
// do something...
}while(!cancelled)
}
}
and call AsyncTask.cancel(true); when you want to stop.
CancelTask task = new CancelTask();
task.execute();
...
task.cancel(true);
And about second question, I want to know how you handle two "Sub-AsyncTask"s.
I will try to find a solution after you update your code.
I have created an Android Application, in that I want to cancel AsyncTask onPause state of Fragment.
I tried using AsyncTask.cancel(true); but it gives null pointer exception.
#Override
public void onPause()
{
super.onPause();
AsyncTask.cancel(true);
}
Thanks.
task.cancel() will do it. Be sure to include frequent checks to isCancelled() in your onBackground() as well as in onPostExecute() to avoid accessing/updating UI which is no longer there.
public void onActivityPause() {
task.cancel();
}
asyncTask.cancel(true); will change a boolean value only and don't stop your thread.
so you need to "ask" if the async task not cancelled.
example:
#Override
protected Void doInBackground(Void... params) {
while(!isCancelled()){
// do something
}
return null;
}
I have a class called "Class A"
a) I have a button called verify
b) onClick of verify i call auth.authenticate() which returns boolean
c) upon true - i have to call an intent
the authenticate function is in another class (auth is a ref)
authenticate function is as follow ()
boolean authenticate() {
new AsyncTask<String, String, String>() {
preExecute()
{
--starting a progress bar--
}
doInBackground(String )
{
---- huge authentication code-----
------ returns true or false -------isVerified
}
onPostExecute(String)
{
--- dismiss progress bar-----------
}
}.execute();
}
--------end of AsynTask block----
synchronized (this) {
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return isVerified; ---- this should return to MyClass - after async finishes
-- but this is returned before async completes.. actually it is correct - because these are thread-- runs parallel, so to make it work - i have waited till async to complete using that synchronized block -- but that will blocking my main thread - (also no progress bar)
I could call my next activity in post execute success - as i m making a library class..i think should not... please help
}
You can override onPostExecute() and return your value from there, if the task was cancelled it will not be called.
AsyncTasks are also threads. So you can make a new AuthenticateAsyncTask<Void,Void,Void> class for authentication and with overriding the onPostExecute method you can call a function with the parameter isVerified
For Ex:
boolean isVerified;
// Async Task
class AuthenticateAsyncTask extends AsyncTask<Void, Void, Void>{
#Override
protected Void doInBackground(Void... params) {
// Authentication
isVerified = returningValue // set your isVerified variable
return null;
}
#Override
protected void onPostExecute(Void result) {
// you can here call a function that indicates the authenticaion value is returned
}
#Override
protected void onPreExecute() {}
}
Hope this helps
The goal:
Using Google App Engine server and Android client, I'm trying to put on the Google map at the Android client Users overlays. Every 30 seconds I'm polling the server and getting Vector that contains users and adding it to the map.
Current status:
I'm dong all that using in one new thread, So after running the app I got:
weird behaviors(delayed overlays, multiple overlays) and after that crushed with ConcurrentModificationException.
After reading a bit i figured out that I need to work with AsyncTask.
Correct me if I'm wrong,But I understand that everything done in the Activity at at onCreate is "running" in UIhread so I need to put the "Logic" (All the Network handling) in doInBackground and all the UI Handling like putting overlays on the map in onPostExecute.
My Question are:
1) In the current status I'm doing:
new Thread()
{
#Override
public void run()
{
super.run();
while(true)
{
SystemClock.sleep(30000);
Vector responseFromServer = getUsersVectorFromServer();
putNewOnlineUserOnTheMap();
}
}
}.start();
What is the right way to convert this To AsyncTask?
Do I poll the server still using new thread in the doInBackground or there is right way to do this?
2) Is there a specific list of what counts as UI to put in onPostExecute or any concepts list?
In my case I guess that in need to put putNewOnlineUserOnTheMap() in onPostExecute.
Thanks.
Something similar to the following:
class UpdateTask extends AsyncTask<Void, Vector, Void>{
#Override
protected Void doInBackground(Void... params) {
// this is running in a background thread.
while (!isCancelled()) {
SystemClock.sleep(30000);
Vector responseFromServer = getUsersVectorFromServer();
// send the result back to the UI thread
// onProgressUpdate will be called then
publishProgress(responseFromServer);
}
return null;
}
#Override
protected void onProgressUpdate(Vector... values) {
// this is executed on the UI thread where we can safely touch UI stuff
putNewOnlineUserOnTheMap(values[0]);
}
}
You can't use the result of the task since the task is finished then. But you can use the progress publishing mechanism to get periodic results. If you use it like that and do the modification on the UI thread you should not get ConcurrentModificationException because you do the modifications on the one thread that can safely modify the UI.
One thing to note here: create new instances of your Vector in the background thread and then use it to update the UI. But don't touch the same object afterwards in the backgroundthread. That way you don't need any synchronization since after the background thread sends it away it is only the UI thread that touches it. (and you could use a simple ArrayList instead of a Vector)
AsyncTask uses generics and varargs.The parameters that are passed to the asyntask are . TypeOfVariableArgumentsParameters is passed into the doInBackground(), ProgressParam is used for progress information and ResultParam must be returned from doInBackground() and is passed to onPostExecute() as parameter.
example:--
protected class ParsingTask extends AsyncTask> {
private ProgressDialog loadingDialog = new ProgressDialog(JsonParserActivity.this);
protected void onPreExecute() {
loadingDialog.setMessage("loading app store..");
loadingDialog.show();
}
#Override
protected ArrayList<Items> doInBackground( Context... params ) {
// do ur process here.
return result;
}
if (!this.isCancelled()) {
}
return result;
}
#Override
protected void onProgressUpdate(String... s) {
super.onProgressUpdate(s);
Toast.makeText(getApplicationContext(), s[0], Toast.LENGTH_SHORT).show();
}
#Override
protected void onPostExecute( ArrayList<Items> response ) {
//if u r dealing with list view and adapters set the adapter here at the onPostExecute()
loadingDialog.dismiss();
}
#Override
protected void onCancelled() {
super.onCancelled();
Toast.makeText(getApplicationContext(), "The operation was cancelled", 1).show();
}
}
You can use AsyncTask like below. Hope this will help you..
Class YourClass{
void YourClass(){
NetworkTask nT = new NetworkTasK();
nT.execute();
}
}
protected class NetworkTask extends AsyncTask<Void, String, Boolean>
{
#Override
protected Boolean doInBackground(Void... params)
{
try
{
String response;
while(keepreceiving)
{
response = in.readLine();//Prog Counter stops here until getting i/p.
if(response != null)
yourFunctionForResponse(response);
}
}
catch (Exception ex)
{
}
return null;
}
private void yourFunctionForResponse(String response){
//things to do....
}
}
You may also try runOnUiThread(Runnable action) along with this to implement your work.