android

multithreading

android-asynctask

android-emulator

As of 15/2/2012 I have yet to find a good explanation to nor a reason why this does not work. The closest to a solution is to use the traditional Thread approach, but then why include a class that does not (seem to) work in the Android SDK?

Evenin' SO!

I have an AsyncTask subclass:

// ParseListener had a callback which was called when an item was parsed in a
// RSS-xml, but as stated further down it is not used at all right now.
private class xmlAsync extends AsyncTask<String, RSSItem, Void> implements ParseListener

That is executed like this:

xmlAsync xmlThread = new xmlAsync();

xmlThread.execute("http://www.nothing.com");

Now this subclass has run into a little error. Previously it did some xml-parsing, but when I noticed that it's doInBackground() wasn't called I stripped it down, line by line, finally ending up with just this:

@Override
protected Void doInBackground(String... params) 
{
    Log.v(TAG, "doInBackground");
        return null;
}

Which, for some reason, logged nothing. However, I added this:

@Override
protected void onPreExecute() 
{
        Log.v(TAG, "onPreExecute");
        super.onPreExecute();
}

And that line is indeed logged when executing the thread. So somehow onPreExecute() is called but not doInBackground(). I have another AsyncTask running in the background at the same time which works just fine.

I'm currently running the app on an emulator, SDK Version 15, Eclipse, Mac OS X 10.7.2, close to the North Pole.

EDIT:

@Override
    protected void onProgressUpdate(RSSItem... values) {

        if(values[0] == null)
        {
                            // activity function which merely creates a dialog
            showInputError();
        }
        else
        {

            Log.v(TAG, "adding "+values[0].toString());
            _tableManager.addRSSItem(values[0]);
        }


        super.onProgressUpdate(values);
    }

_tableManager.addRSSItem() more or less adds a row to a SQLiteDatabase, initialized with the activity's context. publishProgress() is called by the Interface ParseListener's callback. However, since I don't even do anything except log.v in doInBackground() I first found this unnecessary to even bring up.

EDIT 2:

Alright, just to be perfectly clear, this is the other AsyncTask, executing in the same activity and working perfectly fine.

private class dbAsync extends AsyncTask<Void, RSSItem, Void>
{
    Integer prevCount;
    boolean run;

    @Override
    protected void onPreExecute() {
        run = true;
        super.onPreExecute();
    }

    @Override
    protected Void doInBackground(Void... params) {
        // TODO Auto-generated method stub
        run = true;
        prevCount = 0;

        while(run)
        {
            ArrayList<RSSItem> items = _tableManager.getAllItems();

            if(items != null)
            {
                if(items.size() > prevCount)
                {
                    Log.v("db Thread", "Found new item(s)!");
                    prevCount = items.size();

                    RSSItem[] itemsArray = new RSSItem[items.size()];

                    publishProgress(items.toArray(itemsArray));
                }
            }               

            SystemClock.sleep(5000);
        }

        return null;
    }

    @Override
    protected void onProgressUpdate(RSSItem... values) {

        ArrayList<RSSItem> list = new ArrayList<RSSItem>();

        for(int i = 0; i < values.length; i++)
        {
            list.add(i, values[i]);
        }

        setItemsAndUpdateList(list);

        super.onProgressUpdate(values);
    }

    @Override
    protected void onCancelled() {
        run = false;

        super.onCancelled();
    }
}

EDIT 3:

Sigh, sorry I'm bad at asking questions. But here is the initialization of the Tasks.

xmlAsync _xmlParseThread;
dbAsync _dbLookup;

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

_dbLookup = new dbAsync();
_dbLookup.execute();

_xmlParseThread = new xmlAsync();       
_xmlParseThread.execute("http://www.nothing.com", null);
}

Solution 1

You should checkout this answer: https://stackoverflow.com/a/10406894/347565 and the link to google groups it includes.

I had a similar problem as you, still unclear why it is not working, but I changed my code like this and problem is gone:

ASyncTask<Void,Void,Void> my_task = new ASyncTask<Void,Void,Void>() { ... };
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
    my_task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[])null);
else
    my_task.execute((Void[])null);

Solution 2

Matthieu's solution will work fine for most, but some can face problem; unless digging in many links provided here or from web, like Anders Göransson's explanation. I am trying to summarize some other reads right here and quickly explain solution if executeOnExecutor is still working in single thread...

Behavior of AsyncTask().execute(); has changed through Android versions. Before Donut (Android:1.6 API:4) tasks were executed serially, from Donut to Gingerbread (Android:2.3 API:9) tasks executed paralleled; since Honeycomb (Android:3.0 API:11) execution was switched back to sequential; a new method AsyncTask().executeOnExecutor(Executor) however, was added for parallel execution.

In sequential processing all Async tasks run in a single thread and thus have to wait before the previous task ends. If you need to execute code immediately, you need tasks to be processed in parallel in separate threads.

With AsyncTask serial execution is not available between Donut and Honeycomb versions, while parallel execution is not available before Donut.

For parallel processing after Donut: Check the Build version and based on that use .execute() or .executeOnExecutor() method. Following code can help...

AsyncTask<Void,Void,Void> myTask = new AsyncTask<Void,Void,Void>() { ... }; // ... your AsyncTask code goes here
if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.HONEYCOMB)
    myTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
else
    myTask.execute();

NOTE: Function .executeOnExecutor() has checks if targetSdkVersion of project is less than or equal to HONEYCOMB_MR1 (Android:2.1 API:7) then it forces the executor to be THREAD_POOL_EXECUTOR (which runs Tasks sequentially in post Honeycomb).
If you have not defined a targetSdkVersion then minSdkVersion is automatically considered to be the targetSdkVersion.
Hence for running your AsyncTask in parallel on post Honeycomb you cannot leave targetSdkVersion empty.

Solution 3

You can do this by two ways:

Way 1:

if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.HONEYCOMB) // Above Api Level 13
  {
      asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
  }
else // Below Api Level 13
  {
      asyncTask.execute();
  }

In case of way 1 not works for you try way 2.

Way 2:

int mCorePoolSize = 60;
int mMaximumPoolSize = 80;
int mKeepAliveTime = 10;
BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<Runnable>(mMaximumPoolSize);
Executor mCustomThreadPoolExecutor = new ThreadPoolExecutor(mCorePoolSize, mMaximumPoolSize, mKeepAliveTime, TimeUnit.SECONDS, workQueue);
asyncTask.executeOnExecutor(mCustomThreadPoolExecutor);

Hope this will help you.

Solution 4

I had the same issue : can't a execute a second AsyncTask after i called "execute" on a first one : doInBackground is only called for the first one.

To answer why this happens check this answer (different behavior depending on the SDK)

However, for your case, this obstacle can be avoided using executeOnExecutor (available starting from 3.0 worked for me using 4.0.3 ) but beware of limitations of the Thread pool size and queuing.

Can you try something like this :

xmlAsync _xmlParseThread;
dbAsync _dbLookup;

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

_dbLookup = new dbAsync();
_dbLookup.execute();

_xmlParseThread = new xmlAsync();       
_xmlParseThread.executeOnExecutor(_dbLookup.THREAD_POOL_EXECUTOR
 ,"http://www.nothing.com", null);
}

For your update question : it is explained in the docs Basically just to avoid all problems that may come from multithreading like intereference ....

Solution 5

One thing that I would like to know, and it might actually fix your issue, is where are you instantiating the instance of your class and calling the execute() method? If you read the documentation for AsyncTask, both of those operations need to take place on the main UI thread. If you are creating your object and calling execute from some other thread, then onPreExecute might fire, I'm not 100% certain here, but the background thread won't be created and executed.

If you are creating the instance of your AsyncTask from a background thread, or some other operation not taking place on the main UI thread, you could consider using the method: Activity.runOnUiThread(Runnable)

You would need access to an instance of your running Activity to call that method, but it will allow you to run code on the UI thread from some other code that isn't running on the UI thread.

Hope that makes sense. Let me know if I can help more.

David

Solution 6

Android is Brutal! I can't believe this, what flakey implementation that changes from day to today. One day its a single thread, the next its 5 the other is 128.

Anyways here is a nearly drop in replacement for the stock AsyncTask. You can even call it AsyncTask if you wanted to, but to avoid confusion its called ThreadedAsyncTask. You need to call executeStart() instead of execute because execute() is final.

/**
 * @author Kevin Kowalewski
 *
 */
public abstract class ThreadedAsyncTask<Params, Progress, Result> extends AsyncTask<Params, Progress, Result> { 
    public AsyncTask<Params, Progress, Result> executeStart(Params... params){
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB){
            return executePostHoneycomb(params);
        }else{
            return super.execute(params);
        }
    }


    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    private AsyncTask<Params, Progress, Result> executePostHoneycomb(Params... params){
        return super.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params); 
    }
}

Solution 7

I know this may be really late for the thread, but there is a reason why it won't work on later android emulators. When asynctask was introduced android only let you run one at a time, then sometime later, im not sure which version, they allowed you to run multiple asynctasks at once, this caused issues in alot of apps,and so in Honeycomb+ they reverted to only allowing one asynctask to run at a time. Unless you manually change the thread pool. Hope that clears one or two things up for people.

Solution 8

i think its the sdk. i had the same problem, and after changing target sdk from 15 to 11, everything works perfectly.

with sdk15, even though the AsyncTask.Status is RUNNING, the doInBackground is never called. i do think it has something to do with the ui thread though.

Solution 9

Based on Matthieu's answer, below an helper class to execute your AsyncTask correctly depending of the SDK version in order to avoid to duplicate code in your application:

import android.annotation.SuppressLint;
import android.os.AsyncTask;
import android.os.Build;

public class AsyncTaskExecutor<Params, Progress, Result> {

  @SuppressLint("NewApi")
  public AsyncTask<Params, Progress, Result> execute(final AsyncTask<Params, Progress, Result> asyncTask, final Params... params){
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB){
      return asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params);
    }  else{
      return asyncTask.execute(params);
    }
  }

}

Example of use:

public class MyTask extends AsyncTask<Void, Void, List<String>> {

...

final MyTask myTask = new MyTask();
new AsyncTaskExecutor<Void, Void, List<String>>().execute(myTask);