Handling multiple AsyncTask launches - android

I've got AsyncTask in my Activity, when user clicks button, I start an AsyncTask:
#Override
public void onClick(View view) {
switch(view.getId()){
case R.id.btnLogin:
if(task==null){
task=new LoginTask(this);
task.execute();
}
break;
}
}
But if user clicks button after task is completed, I want it to be executed one more time. What should I do accomplish this? Should I create new task every time user clicks button? Is it OK to create new instance if task is already running?
Also, task is static inner class, in order to handle screen rotation.

Declare task as a class variable.And in the postExecute of AsynchTask make it null.Probably you are done

You can execute your AsyncTask's on an Executor using executeOnExecutor()
Now the pool of threads by default run in parallel :
Starting with DONUT, this was changed
to a pool of threads allowing multiple
tasks to operate in parallel. After
HONEYCOMB, it is planned to change
this back to a single thread to avoid
common application erors
To make sure that the threads are running in a serial fashion please use: SERIAL_EXECUTOR.
Misc: :How to use an Executor

If you want it to run only if previous instances have already finished, then you can use AsyncTask.getStatus(), to only start a new task if the status is AsyncTask.Status.FINISHED.

Don't run it at the same time. Make a queue of the Asynctasks and run one after the other.

Related

Android immediately run on ui thread

I have read a lot of other answers on StackOverflow, cant seem to find my answer.
I have a listener in my class, i perform a long running task in an async block:
AsyncTask.execute(() -> {
// long running task here
// ...
listener.updateTitle(newTitle);
});
Once I have the data I pass it back to the relevant class:
#Override
public void updateTitle(String newTitle) {
getActivity().runOnUiThread(() -> title.setText(newTitle));
}
Now from what I researched online calling runOnUiThread(..) adds the runnable to a queue of tasks that need to be run on ui. My problem is this updating the title is more important than any of the other ui tasks running.
Is it possible to add this task to the front of the queue, or execute currently executing task, then execute this new task and continue executing the tasks as they were in the previous order?
Sure:
new Handler(Looper.getMainLooper()).postAtFrontOfQueue(() -> title.setText(newTitle));
It may not be sufficient for your needs though as this action will be executed with some delay anyway. If you need to update the title for the next frame, you may want to consider subclassing TextView and providing a synchronized method for setting text from any thread.
Few things:
If your UI thread is busy that you need to wait for the title to be updated, you need to re-think you app design not find a way to get ahead of the Q.
From the AsynTask, to get something to be run in UI thread, use publishProgress
HTH

How to do more than one AsyncTask one after another?

Let's say I've two AsyncTask.
AsyncTask_A and AsyncTask_B.
I want to execute AsyncTask_B only when AsyncTask_A is totally finished.
I know one that I could execute AsyncTask_B in AsyncTask_A's postExecute() method.
But.. is there any better way?
In this instance you should create a class that acts a singleton that will handle queuing of these tasks (i.e. a list of AsyncTasks within it) where it tracks if any are running, and if they are, queues them instead. Then within the postExecute() of these tasks, all should callback to the queue object to notify them that they are completed, and then within this callback it should then run the next AsyncTask.
This will allow you to:
Call AsyncTaskA without always having to run AsyncTaskB (removing the hard dependency)
Add more AsyncTasks down the track because the dependency on managing these tasks is on the queue object instead of within the AsyncTasks
Queue<ASyncTask> q;
AsyncTask_A async_a;
AsyncTask_B async_b;
q.add(async_a);
q.add(async_b);
while(!q.empty)
{
AsyncTask task = q.pop();
task.execute();
while(task.isRunning())
{
Thread.sleep(SOME_MILLISECONDS) // You can use wait and notify here.instead of sleep
}
}

Android Asynctask Generally Understanding Questions

Android Asynctask Generally Understanding Questions.
If I want to make Asynctask SyncTask, how do I do that?
AsyncTask A = new AsyncTask();
AsyncTask B = new AsyncTask();
A.execute();
B.execute();
If I want A to finish before B starts how should I do that?
If I close an Activity, does the AsyncTask call on that activity gets destroy?
If I close the whole application, does the AsyncTask call on that application gets destroy?
call b.execute() in onPostExecute() of A
AsyncTask is an abstract class so you must extend it in order to add your app specific functionality to it. You implement the doInBackground() method to do what ever work is required. The AsyncTask documentation explains it in detail. I will give a brief answer to each of your question.
If I want to make Asynctask SyncTask, how do I do that?
You have the right idea with creating the async task, however as I mentioned before you have to subclass the async task to actually do some work.
Here is an example (note that the Void types do have meaning however the documentation covers them in great detail)
public class MyTask extends AsyncTask<Void, Void, Void>
{
//This method will run on the back ground thread
#Override
protected Void doInBackground(Void... params)
{
// All the heavy work should be done here e.g.
// loading from network, database etc.
return null;
}
}
Then in your activity you would create and run this task as follows :
MyTask myTask = new MyTask();
myTask.execute()
If I want A to finish before B starts how should I do that?
As the documentation states:
When first introduced, AsyncTasks were executed serially on a single
background thread. Starting with DONUT, this was changed to a pool of
threads allowing multiple tasks to operate in parallel. Starting with
HONEYCOMB, tasks are executed on a single thread to avoid common
application errors caused by parallel execution.
Which means if you are using honeycomb or later async tasks will run on a single thread. So normally A should get executed before B if you execute in that order. But I would do something like this: Launch A, then when onPostExecute() of A gets called, you know that A is done, you can start your task B from this method to be sure that A finishes before B.
If I close an Activity, does the AsyncTask call on that activity gets
destroy?
The short answer to this question is No. The async task will continue to run even if the activity has called on destroy. However if the async task is tied to the activity life cycle you need to make sure you end the task when the activity dies or you will run into problems.
If I close the whole application, does the AsyncTask call on that
application gets destroy?
I am not 100% sure about this one. I believe the behavior is unspecified since now its up to Android to collect resources. It may decide to kill the task right away if its low on resources or it may not. However you should avoid this design. If you need to run something after your application has closed, then look at the Service class.
Take a look at http://www.compiletimeerror.com/2013/01/why-and-how-to-use-asynctask.html#.VNtB1LDF_38 it may help you.

AsyncTask Doesn't Resume When Coming Back to Activity Through ActionBar Back Icon

I have an AsyntTask which starts in onCreate and loops infinitely, updating an UI element.
The update is done in this manner (simplified representation of the Async extending class, let's call it asyncExtender().execute()):
doInBackground(){
while (!isCancelled()) {
Thread.sleep(2000);
publishProgress(someValue);
}
}
protected void onProgressUpdate() {
super.onProgressUpdate(values);
// UI update here, e.g. TextView.setText
}
Another activity is started, let's call it SecondActivity. SecondActivity takes advantage of the hierarchical back button in the action bar to return to the previous activity (NavUtils.navigateUpFromSameTask(this) in onOptionsItemSelected).
The issues is that when I tap on the back button in the action bar in SecondActivity, the AsyncTask seems to not run anymore in the first activity (the UI doesn't get updated every 2 seconds). On the other hand, if I use the hardware "back" button it works fine - the AsyncTask keeps running.
Why the difference in behaviour and how to fix it?
AsyncTasks should not be started simultanously and they also are not designed to be run for too long, at most few seconds. Depending which API you target, AsyncTasks might be run synchronously, so your new AsyncTask will be run only when all previous ones finished, excerpt for docs:
http://developer.android.com/reference/android/os/AsyncTask.html
When first introduced, AsyncTasks were executed serially on a single
background thread. Starting with DONUT, this was changed to a pool of
threads allowing multiple tasks to operate in parallel. Starting with
HONEYCOMB, tasks are executed on a single thread to avoid common
application errors caused by parallel execution.

Running same background task from two activities

In my app I have a background task (using AsyncTask) that downloads stuff from a web site.
This task can be called from two separate activities, and it is possible to call it twice. So in "activity1" the background task "update" is called, and runs for a while (it takes something like 5-10 seconds usually).
Then while it's running, user switches to "activity2" and runs "update" again.
This gives problems: either a crash when both try to clear the database (command: DELETE FROM table) at the same time, causing a "database locked" error. Or they try to put the same item in the database causing a duplicate.
I've tried to solve this by setting a static boolean flag to true when a task is active.
When the task is called, it will check for this flag, and if true (i.e. the same task running on another thread) it goes into a wait loop using a handler until this flag clears, and then returns. This to make sure that when the background task returns, the update has been done. I have to use a Looper for that: this sometimes fails with an error "can create only one looper per thread". And I really have it in a way that only one looper can be started, this is the offending code, which appears at the start of the background task:
if (active) {
Looper.prepare();
handler = new Handler();
handler.postDelayed(new Runnable() {
int count = 0;
#Override
public void run() {
if (active) {
count++;
if (count < 1000)
handler.postDelayed(this, 100);
}
}
}, 100);
Looper.loop();
active = false;
return "done";
}
And to make matters worse it often seems to hang in this loop, without returning.
How to solve such a situation?
Why don't use synchronization instead? It sounds like a concurrency issue. Why don't you make sure that if the first background task is running then the second background task is sleeping until the first one is finished.
Or ensure somehow, that if the user switches to Activity number 2, the background task from activity number 1 is cancelled.
Instead of the AsyncTask you can consider to use IntentService. Have a look at the Android Service concept. The IntentService class ensures that only one request will be processed at one time.
I found this answer very useful during implementing IntentService with Activity callback communication.
Database locking issues solved by wrapping it into a ContentProvider. Besides problems with a method being called again before the previous instance was finished, I had the issue of different methods running in different background threads clashing while trying to write to the database.
Officially designed to allow for sharing data between apps, it also works great for sharing data between threads in a single app. The ContentProvider will make sure that no locking issues occur.

Categories

Resources