Android SDK AsyncTask doInBackground не работает (подкласс)

По состоянию на 15/2/2012 я еще не нашел хорошего объяснения или причины, почему это не работает. Наиболее близким к решению является использование традиционного подхода Thread, но почему же включить класс, который (как представляется, не работает) в Android SDK?

Evenin 'SO!

У меня есть подкласс AsyncTask:

// 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

Это выполняется следующим образом:

xmlAsync xmlThread = new xmlAsync();

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

Теперь этот подкласс запущен в небольшую ошибку. Раньше он выполнял некоторый синтаксический анализ xml, но когда я заметил, что он doInBackground() не был вызван, я разделил его по строкам, наконец, получив только это:

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

Что по какой-то причине ничего не записывало. Однако я добавил следующее:

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

И эта строка действительно регистрируется при выполнении потока. Итак, как-то вызывается onPreExecute(), но не doInBackground(). У меня есть еще одна AsyncTask, работающая в фоновом режиме в то же время, которая отлично работает.

В настоящее время я запускаю приложение на эмуляторе, SDK версии 15, Eclipse, Mac OS X 10.7.2, рядом с Северным полюсом.

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() более или менее добавляет строку в базу данных SQLiteDatabase, инициализированную контекстом активности. publishProgress() вызывается обратным вызовом интерфейса ParseListener. Однако, так как я даже ничего не делаю, кроме log.v в doInBackground(), я впервые обнаружил, что это лишнее даже вызвать.

ИЗМЕНИТЬ 2:

Хорошо, просто для того, чтобы быть совершенно ясным, это другая AsyncTask, выполняющая ту же деятельность и прекрасно работающую.

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();
    }
}

ИЗМЕНИТЬ 3:

Вздох, извините, я плохо спрашиваю. Но вот инициализация задач.

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);
}

Ответ 1

Решение Matthieu будет работать отлично для большинства, но некоторые могут столкнуться с проблемой; если копать во многих ссылках, представленных здесь или из Интернета, например, описание Андерса Геранссона. Я пытаюсь обобщить некоторые другие чтения прямо здесь и быстро объяснить решение, если executeOnExecutor все еще работает в одном потоке...

Поведение AsyncTask().execute(); изменилось в версиях Android. До Donut (Android: 1.6 API: 4) задачи выполнялись серийно, начиная с Donut до Gingerbread (Android: 2.3 API: 9) параллельно; поскольку Honeycomb (Android: 3.0 API: 11) исполнение было переключено на последовательный; однако для параллельного выполнения был добавлен новый метод AsyncTask().executeOnExecutor(Executor).

При последовательной обработке все задачи Async запускаются в одном потоке и, следовательно, должны ждать до окончания предыдущей задачи. Если вам нужно немедленно выполнить код, вам потребуются задачи, которые будут обрабатываться параллельно в отдельных потоках.

С AsyncTask серийное исполнение недоступно между версиями Donut и Honeycomb, в то время как параллельное выполнение недоступно до Donut.

Для параллельной обработки после Donut: проверьте версию сборки и на основе использования метода .execute() или .executeOnExecutor(). Следующий код может помочь...

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: Функция .executeOnExecutor() имеет проверки, если targetSdkVersion проекта меньше или равен HONEYCOMB_MR1 (Android: 2.1 API: 7), тогда он заставляет исполнителя THREAD_POOL_EXECUTOR (который последовательно запускает задачи в post Honeycomb).
Если вы не определили a targetSdkVersion, то minSdkVersion автоматически считается targetSdkVersion.
Следовательно, для одновременной работы вашей AsyncTask в post post Honeycomb вы не можете оставить targetSdkVersion пустым.

EDIT: Библиотека поддержки Android теперь включает вспомогательный класс AsyncTaskCompat, который может позаботиться об изменениях в API и сделать ваш код немного меньше/проще. Вот синтаксис...

AsyncTask<Void,Void,Void> myAsyncTask = new AsyncTask<Void,Void,Void>() { ... };    // ... your AsyncTask
AsyncTaskCompat.executeParallel( myAsyncTask, Params );

Где myAsyncTask - ваша заданная задача или вы можете сделать new AsyncTask() внутри инструкции здесь, Params - это параметры, которые нужно отправить в AsyncTask, или вы можете отправить null.

AsyncTaskCompat устарел на уровне API 26.0.0-alpha1

Ответ 2

Вы должны проверить этот ответ: fooobar.com/questions/64008/... и ссылку на группы google, которые он включает.

У меня была такая же проблема, как и у вас, до сих пор неясно, почему она не работает, но я изменил свой код, как это, и проблема исчезла:

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);

Ответ 3

У меня была такая же проблема: не удается выполнить вторую AsyncTask после того, как я вызвал "выполнить" на первом: doInBackground вызывается только для первого.

Чтобы ответить, почему это происходит, проверьте этот ответ (различное поведение в зависимости от SDK)

Однако для вашего случая это препятствие можно избежать с помощью executeOnExecutor (доступный начиная с 3,0, работал у меня, используя 4.0.3), но остерегайтесь ограничений размера пула и очереди.

Можете ли вы попробовать что-то вроде этого:

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);
}

Для вашего вопроса об обновлении: это объясняется в docs В основном, чтобы избежать всех проблем, которые могут возникнуть в результате многопоточности, например, intereference....

Ответ 4

Одна вещь, которую я хотел бы знать, и это может действительно исправить вашу проблему, - это где вы создаете экземпляр своего класса и вызываете метод execute()? Если вы прочитали документацию для AsyncTask, обе эти операции должны выполняться в основном потоке пользовательского интерфейса. Если вы создаете свой объект и вызываете выполнение из какого-то другого потока, то onPreExecute может срабатывать, я не уверен на 100% здесь, но фоновый поток не будет создан и выполнен.

Если вы создаете экземпляр вашей AsyncTask из фонового потока или какую-либо другую операцию, которая не выполняется в основном потоке пользовательского интерфейса, вы можете рассмотреть возможность использования метода: Activity.runOnUiThread(Runnable)

Вам понадобится доступ к экземпляру вашей текущей активности для вызова этого метода, но он позволит вам запускать код в потоке пользовательского интерфейса из другого кода, который не работает в потоке пользовательского интерфейса.

Надеюсь, что это имеет смысл. Дайте мне знать, если я могу помочь больше.

Дэвид

Ответ 5

Вы можете сделать это двумя способами:

Способ 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();
  }

В случае способа 1 не работает для вас, попробуйте способ 2.

Способ 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);

Надеюсь, это поможет вам.

Ответ 6

Android жесток! Я не могу в это поверить, какая плохая реализация меняется с сегодняшнего дня. Однажды его единственный поток, следующий его 5 другой - 128.

В любом случае, это почти падение в замене запаса AsyncTask. Вы даже можете назвать это AsyncTask, если хотите, но чтобы избежать путаницы, это называется ThreadedAsyncTask. Вам нужно вызвать executeStart() вместо выполнения, поскольку execute() является окончательным.

/**
 * @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); 
    }
}

Ответ 7

Я знаю, что это может быть очень поздно для потока, но есть причина, почему он не будет работать на более поздних эмуляторах Android. Когда была введена asynctask, андроид только позволял вам запускать по одному, а затем через некоторое время, я не уверен, какая версия, они позволили вам запускать сразу несколько асинтактов, это вызвало проблемы во многих приложениях, и поэтому в Honeycomb + они вернулись только к позволяя одновременно запускать одну асинтеску. Если вы вручную не измените пул потоков. Надеюсь, что очистит одну или две вещи для людей.

Ответ 8

Я думаю, что это sdk. У меня была та же проблема, и после изменения целевого sdk с 15 до 11 все работает отлично.

с sdk15, даже если AsyncTask.Status работает, doInBackground никогда не вызывается. я действительно думаю, что это имеет какое-то отношение к нити ui, хотя.

Ответ 9

На основе ответа Matthieu ниже класса помощник, чтобы выполнить ваш AsyncTask правильно в зависимости от версии SDK, чтобы избежать дублирования кода в вашем приложении:

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);
    }
  }

}

Пример использования:

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

...

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