Finishing an AsyncTask when requested - android

I am creating a game where the user interacts with the GUI while the computer finds solutions to the puzzle in a background task. The user can choose to end the game any time, at which point I want to stop my background task and retrieve the solutions it found.
This is all working fine except that I can't force the background task to end when the user chooses?!? When the user selects "Done" I have the following:
computerSolutionTask.cancel(true);
// ...disable some GUI buttons etc...
while (computerSolutionTask.getStatus() != AsyncTask.Status.FINISHED){
// ...do nothing...
}
txt_computer.setText(computerSolutionTask.get());
And in my AsyncTask class I am checking "isCancelled()" regularly but it just seems to hang in the while loop I included above.
I feel that I may be going about this whole thing a little incorrectly because I don't really want to cancel the background task I just want it to finish wherever it's up to and return what it has.
This thread appears to be asking the same question I am but has no solution...I'm drawing blanks with all my research thus far and any help would be much appreciated.

A running AsyncTask that is cancelled will never reach onPostExecute and so it will never 'finish'. You don't need to wait for it anyway, if your particular logic requires it use regular synchronization methods.

You need to put the condition inside the doInBackground() method to check whether the AsyncTask is cancelled or in running state.
protected Object doInBackground(Object... x)
{
while (/* condition */)
{
// work...
if (isCancelled()) break;
} return null; }

Related

Working in the DoInBackground or in the PostExecute of an Asynctask

I'm working in an Android application that is using Microsoft Azure Face Api to get some information from an image. After analizing all the people in the image I get the results in the postExecute() call correctly, but now I need to do some changes if I detect an specific person (all the work is different if this specific person is detected).
I correctly detect this person but I want to know if I can do my work in the DoInBackground() so that I don't need to wait for the result (because if I detected this person I need to send a socket message and the result is not valid).
I actually receive a full list of people in the onResult, then I look through all this list to find the specific person and send the socket message. I want to know if I can send this message as soon as I detect this person in the DoInBackground() and cancel the rest of the execution.
Considering the official documentation :
doInBackground(Params...), invoked on the background thread immediately after onPreExecute() finishes executing. This step is used to perform background computation that can take a long time. The parameters of the asynchronous task are passed to this step. The result of the computation must be returned by this step and will be passed back to the last step. This step can also use publishProgress(Progress...) to publish one or more units of progress. These values are published on the UI thread, in the onProgressUpdate(Progress...) step.
onPostExecute(Result), invoked on the UI thread after the background computation finishes. The result of the background computation is passed to this step as a parameter.
A task can be cancelled at any time by invoking cancel(boolean). Invoking this method will cause subsequent calls to isCancelled() to return true. After invoking this method, onCancelled(java.lang.Object), instead of onPostExecute(java.lang.Object) will be invoked after doInBackground(java.lang.Object[]) returns. To ensure that a task is cancelled as quickly as possible, you should always check the return value of isCancelled() periodically from doInBackground(java.lang.Object[]), if possible (inside a loop for instance.)
So if you want to stop the task you can do something like:
class myAsyncTaskClass(val pictures:List<Pictures>, Void, Boolean){
fun doInBackground(var args){
// Check all your pictures, if you find the right face stop your task
if (specificPersonIsDectected){
cancel()
return specificPersonIsDectected
}
}
fun onCancelled(var specificPersonIsDectected){
//Notify you found the specific person
}
}
I encourage you to read the official documentation to understand how you should work with AsyncTask
You need use break like this
if (isCancelled()) break;
inside doInBackground method

How to update the UI smoothly?

So from what I've read, Android's AsyncTask is a great way to asynchronously load information from the Internet. However, I don't want to block up the UI and prevent the user from interacting with it.
A basic description of my problem.
Currently, I am using websockets in order to send/receive data from a web server. On events like a user entering the room, a song being added or removed from a playlist, a song being upvoted or downvoted, or one song ending and another one beginning, the UI must be updated in order to indicate changes. But ideally, these changes will be occurring very frequently, which means that constantly blocking the UI in order to refresh it would be cumbersome and annoying.
How would I update my UI without interrupting the user in their activities? Would AsyncTask suffice?
The asyncTask does not block the UI. It runs on a separate thread to send / receive the data from the web, and then returns the results. When you receive the results back, you can update the UI as you choose.
Your UI will not be stopped while the asyncTask is performing its background work. You can try it out by by building one in your activity and simply sleeping for some amount of time (let's say five seconds) in the doInBackground method. You will see that your UI is still functional during that five seconds.
Edit: You can do just about anything with the results you get back and it won't interrupt your UI either. If that's not the case, you'll probably want to look at optimizing what you are doing with your in memory objects. Anything not stored in memory should probably be retrieved or written to disk, database, or internet endpoint with an AsyncTask. As the commenter points out above, this is not the only way to use other threads, but it's easy and will probably work if you're making a reasonable web request and expect users to have a decent connection. You will just want to make sure you have timeouts and exceptions covered so that your app doesn't crash if the task takes longer than expected.
public class LoadCommentList extends AsyncTask<Integer, Integer, List<Comment>> {
private String commentSubject;
public LoadCommentList(commentSubject){
this.commentSubject = commentSubject;
}
// Do the long-running work in here
protected List<Comment> doInBackground(Integer... params) {
// the data producer is a class I have to handle web calls
DataProducer dp = DataProducer.getInstance();
// here, the getComments method makes the http call to get comments
List<Comment> comments = dp.getComments(commentSubject);
return comments;
}
// This is called each time you call publishProgress()
protected void onProgressUpdate(Integer... progress) {
// setProgressPercent(progress[0]);
}
// This is called when doInBackground() is finished
protected void onPostExecute(List<Comment> comments) {
// calls a method in the activity to update the ui
updateUI(comments);
}
}
There are cleaner examples actually using the Integer... params for example, but this is just something I had handy as an example.
I don't know where you read that but asyn task are worst way to make web service call this days. You should use Retrofit for service call, it is 8 Times faster and handle UI update smoothly.
Read more about this here :-
http://googleweblight.com/?lite_url=http://instructure.github.io/blog/2013/12/09/volley-vs-retrofit&ei=qR4bQU5c&lc=en-IN&s=1&m=260&host=www.google.co.in&ts=1465531978&sig=APY536z0v15lfX3G6KY4nls4wf1kzttJdA

sleep in android doesn't work

Well I just want to press a button and a countdown appears in it however when I press the button the program stops and finally shows the number 1 but doesn't show 3 or 2.
btnTurno.setOnClickListener(new View.OnClickListener()
{
public void onClick(View v) {
btnTurno.setText("3");
SystemClock.sleep(1000);
btnTurno.setText("2");
SystemClock.sleep(1000);
btnTurno.setText("1");
}
});
What I'm doing wrong?
First of all, Sleep should be called as Thread.sleep(x);
But on the other hand , sleep is NOT recommended since it will block the user interaction with the application.
If you want to make a countdown ( as it looks like what you are doing ), you should look at this
http://developer.android.com/reference/android/os/CountDownTimer.html
onClick is being executed in single handler message on GUI thread, so each time you change text, it overwrites previous value, so system is not able to redraw view.
Also you should not call sleep() on main gui thread, that will cause ANR - application is notresponding
Setting the text and drawing the text are separate operations that both happen on the same thread. You're setting the text three times before the framework gets the chance to draw anything, so all you see is the last change.
For this to work, you'd need to do your text updates and sleeps on a separate thread. However, you can't make UI calls directly on a different thread, so you need to post your messages.
One approach would be to create an AsyncTask, and use your code above but call publishProgress() where you're currently calling setText(). The publishProgress() call in the async thread results in onProgressUpdate() running in the UI thread; from there, you can call setText() to update the UI.
Edit: the CountDownTimer approach mentioned in a different answer is better.

How to cancel an asyncTask and executing it multiple times?

I'm doing an app which use the new Google Maps V2, when the Map Camera moves, I need to reload the points to the new camera position, I do it with a asynctask.
The problem is when I move the camera position multiple times, app loads the points multiples times. So, I cancel the async task when the camera is moved and I don't load new point until the task is cancelled. I have an empty while loop to do it, Is there a better solution to do it?
// LOAD NEW POIS ASYNC
private void updatePoisInMap( ){
....
if (refresh_pois_async != null) {
refresh_pois_async.cancel(true);
while (!refresh_pois_async.isCancelled()) {
}
}
refresh_pois_async = new RefreshPoisAsync( ).execute( lowerLeftCorner, topRightCorner);
}
Actually, I don't think your while loop will work, or at least not as you would expect it to. The AsyncTask.isCancelled() method is normally used inside the doInBackground() method, to support early cancellation. You have several options to do what you want:
override the onCancelled() method of your AsyncTask. This is guaranteed to be called on the main thread when your doInBackground() is finished and you called cancel() on your task beforehand. In your onCancelled() you can then start a new AsyncTask. Use the isCancelled() in your Task's doInBackground() for early exit, as described above.
don't cancel the current task, but let it run. In the meantime the user may move the map multiple times. As long as a task is still running, just set a flag that it should be rerun. At task completion (override onPostExecute() in your AsyncTask) check the flag and start over if it was set.
combine the above to find the right balance between responsiveness (start loading the right POIs quick enough) and over-processing (don't start every time, but wait for the user to stop scrolling).

How to cancel Service/IntentService/AsyncTask/Looper

I'm going nuts here.
I want a simple thing - I have a long task (fetching several data objects from the web) and I want the ability to cancel it.
I tried a lot of things (a lot) and nothing works
The flow goes like this:
the user click on a button
I start the work (I tried with AsyncTask, Service, IntentService and Looper)
the task takes care of everything including adding ongoing notification for progress updates
the intent in the notification has a call for a new activity that her only purpose is to cancel the ongoing task
in the cancelActivity I tried to call stopService() for Service/IntentService or do
Looper.quit() for the Looper (I don't remember what I tried for AsyncTask, not sure if there is such api for canceling it)
In my point of view the best option will be using IntentService (I could have several task lining up and IntetService will do it in order like I want) but
I'm open to suggestions for any type of implementation - I don't care what the code will be, just that I will have the option to cancel the task
Thank you in advance for your help
Dror
(I'm off to bed - 8 hours on the same issue is just too much)
It does not matter what specific operation you use to stop the task if you don't recognize the stop condition in your background logic. The only way to cleanly accomplish it is if background worker stops and exits in good faith.
There are few possible scenarios and solutions that you can use for canceling background work.
Background thread executes many short steps (for example computation with some loops). In this case, check some flag (could be isInterrupted()) between operations and exit if this flag indicates that operation must stop.
Background thread is waiting on monitor. Call interrupt() on background thread, catch exception in in exception handler make appropriate steps to finish this task cleanly and exit.
Background thread is waiting on IO. This use case is very hard to solve in general case. If you use some socket, you can try closing this socket externally and catch the exception. In worst case scenario, you can just abandon the thread in the state that if it ever returns from IO it knows that it is canceled and IO results must be discarded. If you do it often - you will run out of memory, so I would not really recommend it.
In any case, there is no way (except killing the thread which is really bad) to stop your task if it does not know about possibility of being stopped.
Ok, i manged to do something close to what I want.
I'm using IntentService that will queue my task. each new task is AsyncTask. the AsyncTask starts with sending notification with pendingIntent for cancelActivity.
When clicking on the notification the user gets a warning popup about stopping the task. If he clicks yes than I do stopService() on my IntentService.
In the IntentService I added:
#Override
public void onDestroy() {
currentTask.cancel(true);
if (mNotificationHelper != null)
mNotificationHelper.completed();
super.onDestroy();
stopSelf();
}
in the AsyncTask I added:
#Override
protected void onCancelled() {
isCanclled = true;
httpClient.getConnectionManager().shutdown();
mNotificationHelper.completed();
if (mAsyncTaskListener != null)
mAsyncTaskListener.AsyncTaskCanceled();
}
so that will drop all the connection currently in motion. In the actual code that do the work I need to catch the exception for connection shutdown and handle it
so in the end I'm not actually stopping the AsyncTask/Service but rather exposing the httpClient so I will be able to drop the connection in asynchrony why.
I think is a bit ugly but I got no other way
thank you all

Categories

Resources