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
Related
Basically title. I can run them all in a row or all at once. I need the first one to run to load data for the rest.
Any ideas?
Maybes using handler for the first one so that the code runs on a different thread and trigger the rest when that one completes:
Handler firstTask = new Handler(new Runnable() {
Run() {
//do code
//run rest of tasks
}
}
If you want to make sure that the first AsyncTask has finished and returned the required data before the rest are executed, then override the onPostExecute() method of the first AsyncTask and execute the remaining AsyncTasks inside it.
onPostExecute() is a methode called after the AsyncTask is finished, you can check for the correctness of the received data inside it before executing the other AsyncTasks also inside it.
Your AsyncTasks will be run in the order in which they are submitted and not concurrently, unless you explicitly use the ExecuteOnExecutor method. You can pass data between them accordingly.
Just to be clear, you don't have to do anything at all to make sure that the first task completes before the second (and so on) are run. Each will complete before the next is started, in submission order.
I have an existing code which has an Async Task used for some request-response.
In the post execute method it sets the parsed data into some db.
Now I need to modify this code in such a way that at app launch, the data gets downloaded one by one.
i.e. I need to execute task A, then on its full completion (even the data is set) I need to start task B and so on for around 12 tasks.
Note: I need to finish the "post execute" as well before starting the next task.
I am not sure how to do this.
Please suggest.
You can do it with myAsyncTask.executeOnExecutor (SERIAL_EXECUTOR)
AsyncTask a = new AsyncTask();
AsyncTask b = new AsyncTask();
AsyncTask c = new AsyncTask();
a.executeOnExecutor (SERIAL_EXECUTOR);
b.executeOnExecutor (SERIAL_EXECUTOR);
c.executeOnExecutor (SERIAL_EXECUTOR);
Now the exection order will be a -> b -> c;
This one gets a bit messy..
Configuring a AsyncTask with SERIAL_EXECUTOR will indeed force serial execution of background logic,
that is, the logic contained within the doInBackground() calls.
SERIAL_EXECUTOR makes no guarantees as to when the onPostExecute() will be invoked.
Executing a set of AsyncTask in SERIAL_EXECUTOR mode may result in onPostExecute() of task A being
executed after the doInBackground() of task B.
If the latter demand is not crucial to your system, just use SERIAL_EXECUTOR and be happy.
But, if it is, you will need to change your architecture in a way that will force
such demand.
one way of doing so will be as follows:
class MyAsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... params) {
// do background work
return null;
}
#Override
protected void onPostExecute(Void args) {
// update UI here
new MyNextAsyncTask.execute(..); // <---------- start next task only after UI update has completed
}
}
That is: you will wait for the UI update of one operation to complete before starting the next one.
Another way, which I would probably prefer, would be to manage th entire task flow
within a single thread that internally perform att background tasks A -> B -> C ....
and, in between, issues UI update commands and waits on a ConditionVariable for these
tasks to complete
new Thread() {
ConditionVariable mCondition = new ConditionVariable(false);
void run() {
do_A();
runonUIThread(updateAfter_A_Runnable);
mPreviewDone.block(); // will be release after updateAfterA_Runnable completes!
do_B();
runonUIThread(updateAfter_B_Runnable);
mPreviewDone.block(); // will be release after updateAfter_B_Runnable completes!
etc..
}
}
Hope it helps.
The implementation depends on your requirement.
If after every call you need to update your UI you can download and save data in doInBackground() and the update in onPostExecute().
If you wanna update your UI just once then download all the data inside a loop inside your doInBackground() and also save your data in db there only and finally call your onPostExecute() just once.
If it has nothing to do with updating the UI then you can simply use Thread.
NOTE : AsyncTask order of execution will be serial only above HoneyComb below that it would be parallel.
My App contains a function that takes time to load ( parsing files).
THe function is called at multiple user case, i.e. from multiple user triggered condition.
Besides, it is called when onCreate is called.
In simple word, the flow is:
User click/OnCreate trigger
Function to parse file
Post to windows
Other postprocessing
I hope the user can click cancel to stop parsing files.
I tried to use asynctask. I know I can put the function to onPostExecute.
But I assume onPostExecute is just for dismiss progress dialog. Or I have to move a lot of codes ( for different cases) to it. Not a good idea.
I do not suppose user to do anything during parsing files.
So, what is the best way to do so? Despite I know it is not good, I think i have to occupy the UI thread.
In simple word, I want to wait for "parsing files", but i do not want to occupy the UI thread, so user can click cancel.
update:
I tried. however, there is a problem:
I use asynctask. I called:
mTask = new YourAsyncTask().execute();
YourAsyncTask.get(); // this force to wait for YourAsyncTask to return.
DoSomethingBaseOnAsyncTaskResult();
YourAsyncTask.get() hold the UI thread. So, there is not loading dialog, and user cannot click cancel from the dialog. It seems I have to move every line after
mTask = new YourAsyncTask().execute();
to
OnPostExecute()
which i did not prefer to do so because DoSomethingBaseOnAsyncTaskResult() can be very different based on the return result. or else, it becomes do everything in YourAsyncTask()
AsyncTasks should ideally be used for short operations (a few seconds at the most.)
When an asynchronous task is executed, the task goes through 4 steps:
onPreExecute(), invoked on the UI thread before the task is executed. This step is normally used to setup the task, for instance by showing a progress bar in the user interface.
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.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.
onProgressUpdate(Progress...), invoked on the UI thread after a call to publishProgress(Progress...). The timing of the execution is undefined. This method is used to display any form of progress in the user interface while the background computation is still executing. For instance, it can be used to animate a progress bar or show logs in a text field.
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.
CODING
To start an Async task
mTask = new YourAsyncTask().execute();
and to cancel that task
mTask.cancel(true);
More detail is available here
In order to use the AsyncTask API, one has to follow the steps described below:
Create a class which extends AsyncTask.
Fill in the generic types available as generics in the class for:
the task execution array parameters
progress array parameters
result array parameters
Implement the method doInBackground(Parameters... parameters). This
method must execute the job which is supposed to be quite demanding.
Optionally, one can implement methods for:
cancelling the task - onCancelled(...)
executing tasks before the demanding task - onPreExecute(...)
reporting progress - onProgressUpdate(...)
executing activities after the demanding task is finished
-onPostExecute(...).
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.
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.