9

I'm trying to start an AsyncTask from another AsyncTask's doInBackground()-method...

Is this even possible? If yes, how can I achieve this?

LATER EDIT:

The idea is that I start an asynctask that will get me statuses from Tweeter ... Everything ok until here ... But when I encounter some specific tweets I need to modify them with some info from my server, here I will make another network operation, and since I need to wait until I get the info from my server I do:

GetContent getContentServiceAsyncTask = new GetContent(context);
try {
    tweetText = Uri.decode(getContentServiceAsyncTask.execute(
            URL_GET_CONTENT, jsonRequest).get());
} catch (InterruptedException e) {
    Log.e(TAG_DEBUG, "InterruptedException: ", e);
} catch (ExecutionException e) {
    Log.e(TAG_DEBUG, "ExecutionException: ", e);
}

This is started from the allready started AsyncTask in doInBackground() method ...

I know I can just add the methods in the AsyncTask and just call them in the doInBackground() method, but I need to use them in other places, where I start these AsyncTasks from onPostExecute ...

If you guys think there is an easy work-around for this, that won't affect my performance, that will be great ... if not I will make some static methods that I will call in all the AsyncTasks I need(but this will require me to modify a lot of my code)

JJD
  • 50,076
  • 60
  • 203
  • 339
Ionut Negru
  • 6,186
  • 4
  • 48
  • 78
  • why would you want to do that? http://developer.android.com/reference/android/os/AsyncTask.html. check the topic threading rules – Raghunandan Oct 22 '13 at 14:28
  • It is possible but it will crash if you try to modify the UI from the onPostExecute on the second task as it is not executing in the main thread. – Juangcg Oct 22 '13 at 14:29
  • IF your second task not contains UI operation, you can start. – nurisezgin Oct 22 '13 at 14:30
  • 1
    Its advisable to start async in post execute of first async. – Subburaj Oct 22 '13 at 14:30
  • If any of the given answers was helpful, feel free to accept :) – Langusten Gustel Oct 22 '13 at 15:41
  • @nurisezgin my second task does not contain UI operation ... can you explain how I can do it ? – Ionut Negru Oct 22 '13 at 17:49
  • 1
    Hi Negru, your first async task must run on parallel task artitecture. Thus you start your first task with executor ==> "new FirstTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, );" Second task run in your firsttask's doInbackgreound(). I will create sample app and write answer. – nurisezgin Oct 23 '13 at 04:59
  • I don't need parallel execution, because as you can see I use get() for the inside AsyncTask... also the executor was added in API level 11 which is no good for me because my app has support for lower levels also .. But still a good answer ... thank you – Ionut Negru Oct 23 '13 at 08:27

7 Answers7

15

AsyncTasks are supposed to run from main (UI) thread. You should not run another AsyncTask from doInBackground because this method is executed on a non-UI thread.

In your case, I can suggest you two things:

  1. Combine the processing of your both AsyncTaks in a single request
  2. Launch your second AsyncTask from onPostExecute of your first task.
waqaslam
  • 67,549
  • 16
  • 165
  • 178
  • 2
    @downvoter: mind to explain the reason for downvote? – waqaslam Oct 22 '13 at 14:34
  • I can't do that ... I need a blocking AsyncTask, that I call with .get() ... I can't wait until the current AsyncTask is finished and do the update in onPostExecute(). – Ionut Negru Oct 22 '13 at 17:26
11

According to the post below you can do Activity.runOnUiThread() to run a Runnable on the main-Thread (from another thread).

So theoretically you could do this:

  • Run the Async Task
  • do Activity.runOnUiThread(Runnable) inside your AsyncTask and start a new AsyncTask from inside of this runnable

As the name says Activity.runOnUiThread() executes the runnable on the main-thread

But it's kind of hacky.

Code should look something like this: (didnt test)

// first task
    (new AsyncTask<String, String, String>() {

        @Override
        protected String doInBackground(String... params) {
            ParentActitity.this.runOnUiThread(new Runnable() {

                @Override
                public void run() {
                    //second async stared within a asynctask but on the main thread
                    (new AsyncTask<String, String, String>() {

                        @Override
                        protected String doInBackground(String... params) {
                            // TODO Auto-generated method stub
                            return null;
                        }

                    }).execute();

                }
            });
            return null;
        }

    }).execute();

This nested example is not a good style to use in production because (IMHO) its close to unreadable.

Additional Notes:

Activity.runOnUiThread(Runnable) is not static! Thats why my example uses ParentActivity(.this).runOnUiThread(Runnable).

Community
  • 1
  • 1
Langusten Gustel
  • 10,917
  • 9
  • 46
  • 59
  • And how should I run this ? Keep in mind that I'm in an AsyncTask class like this : `public class DoSomeBackgroundWork extends AsyncTask {...}` – Ionut Negru Oct 22 '13 at 17:25
  • Thank you jmeier, but I get the following warning: "No enclosing instance of the type TwitterActivity is accessible in scope" – Ionut Negru Oct 22 '13 at 21:09
  • You have to call it on a reference to your TwitterActivity instance. Do you do that? – Langusten Gustel Oct 22 '13 at 21:14
  • No, but in the end I think this will be too much trouble and bad for performance, so I created the methods that I needed to call directly in this AsyncTask ... Still, I think you answer was closest to what I was looking for, so I will accept it. Thank you. – Ionut Negru Oct 22 '13 at 22:42
6

You can't do it in doInBackground() but you can from onProgressUpdate() for example since it runs on the UI thread. Just keep in mind that after 3.0 they will be put on the same queue and will not run in parallel, see executeOnExecutor

Dan Pena
  • 87
  • 5
0

See the documentation for AsyncTask() and AsyncTask#execute(). Both state that they must be invoked on the main UI thread.

Alex Lockwood
  • 83,063
  • 39
  • 206
  • 250
  • I know that, that's why I asked if you guys know some work-around for this! – Ionut Negru Oct 22 '13 at 17:28
  • 1
    @lonutNegru Your question was "is this even possible". My answer is "no" (at least not directly). You'll have to post a runnable on the main thread, containing the code that executes the new `AsyncTask` instead. jmeier's answer gives one possible way to do this. – Alex Lockwood Oct 23 '13 at 04:34
  • Yes, thank you Alex Lockwood, in the end I chose to modify the structure to avoid this issue and to be safe in the future :) ... – Ionut Negru Oct 23 '13 at 08:23
0

Basically sample I wrote;

public class MainActivity extends Activity {

View view;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    view = new View(this);
    setContentView(view);

    view.setBackgroundColor(Color.RED);

    new FirstTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, null);
}

private class FirstTask extends AsyncTask<Void, Void, Void>
{
    @Override
    protected Void doInBackground(Void... params) {

        new SecondTask().execute();
        try {
            Thread.currentThread().sleep(1000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        Log.e("task","first");
        return null;
    }

    @Override
    protected void onPostExecute(Void result) {
        view.setBackgroundColor(Color.GREEN);   
    }

}

private class SecondTask extends AsyncTask<Void, Void, Void>
{
    @Override
    protected Void doInBackground(Void... params) {
        Log.e("task","second");
        return null;
    }
}

}
nurisezgin
  • 1,530
  • 12
  • 19
0

Its simple, you have to use an Executor:

new AsyncTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, new String[]{"param 1", "param 2"});

This method is typically used with THREAD_POOL_EXECUTOR to allow multiple tasks to run in parallel on a pool of threads managed by AsyncTask, however you can also use your own Executor for custom behavior.

Warning: Allowing multiple tasks to run in parallel from a thread pool is generally not what one wants, because the order of their operation is not defined. For example, if these tasks are used to modify any state in common (such as writing a file due to a button click), there are no guarantees on the order of the modifications. Without careful work it is possible in rare cases for the newer version of the data to be over-written by an older one, leading to obscure data loss and stability issues. Such changes are best executed in serial; to guarantee such work is serialized regardless of platform version you can use this function with SERIAL_EXECUTOR.

-1

it's not be better to take those couple of inet actions and involved them by start a separate Thread which will handle this part of data management ? google everywhere put it's patterns which if u look closest they all are derived from simple classes... this API makes android like a spider web callback involving callback... Try to understand basics of java and it will not be difficult to write a piece of good code. Services/content resolvers/receivers/loaders are limited somehow... Threads are almost able to achieve anything what u wish to...

Most of you uses the code without thinking and not even try to understand how it work, and understanding what is the programming key. I try not to use code whose basics i dont know. And if i need to I studied it thoroughly. I used Java for black magic now I think it's DATA FLOW A-> B-> C etc .. good FLOW is essential.

EDIT:

yes i wrote the solution for youre problem

there is many ways to achive that what u want but it's not my response to writer code for you

  • you can use IntentService class it has a built-in queue --- just use separate startService() calls for each transfer.

  • you can use Thread - my prefered way

  • you can use Service

  • you can use AsyncTaskLoader

  • but don't use ASYNC TASK if u need fire more than TWO linked with each other at ONCE they all on MODERN PHONES RUNS as QUEUE on a single thread (ps. you can run them on EXECUOR as a workaround)

Since JAVA 1.5 tasks are separated from threads - there was added

what do du mean by custom thread

THREAD (as we speak of it) is not a THREAD CLASS OBJECT but it is a TASK defined in run() method of THREAD CLASS so any CLASS DERIVED from THREAD STILL STARTS THREAD (aka TASK)

    public synchronized void start() {
        checkNotStarted();

        hasBeenStarted = true;
        /** 
         * look at this line i'ts rerouting run() code of youre thread class  
         * to native method from there thread is created in pure C code 
         */
        nativeCreate(this, stackSize, daemon);
    }

like this for example in POSIX :

int  pthread_create(pthread_t  *  thread, pthread_attr_t * attr, void *
   (*start_routine)(void *), void * arg);

from this point we can talk about MULTITASKING when OS is performing multiple tasks over a certain period of time by executing them concurrently

since JAVA 1.5 ther is a Executor interface which provides a way of decoupling task submission from the mechanics of how each task will be run, including details of thread use, scheduling, etc. Executor is normally used instead of explicitly creating threads. You have many executors one of them is: ThreadPerTaskExecutor

class ThreadPerTaskExecutor implements Executor {
      public void execute(Runnable r) {
          new Thread(r).start();
      }
  }

This executor spawns a new thread for each task.

getting back to AsyncTask:

AsyncTaskM<P, P, R>.executeOnExecutor(Executor,P);

allow multiple tasks to run in parallel on a pool of threads managed by AsyncTask, however you can also use your own Executor for custombehavior.

and yes if You do not feel up to it and do not know enough beeter to someone else do it :)

I hope that this addition change your mind about your vote.

ceph3us
  • 7,326
  • 3
  • 36
  • 43
  • Your response doesn't give any way to achieve this, this is why I down voted. There are ways to do this better, interfaces, custom Threads, broadcasts, but at the time I posted the question I wasn't aware of many of them. I also recommend EventBus for decoupling components. – Ionut Negru Jul 03 '15 at 04:10