Android SDK AsyncTask doInBackground не работает (подкласс) - PullRequest
89 голосов
/ 03 февраля 2012

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

Вечером ТАК!

У меня есть подкласс 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 () более или менее добавляет строку в базу данных SQLite, инициализированную в контексте действия. 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);
}

Ответы [ 9 ]

158 голосов
/ 16 августа 2012

Вы должны получить этот ответ: https://stackoverflow.com/a/10406894/347565 и ссылку на группы 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);
106 голосов
/ 16 июля 2014

Решение Мэтью будет работать нормально для большинства, но некоторые могут столкнуться с проблемой; если не копаться во многих ссылках, предоставленных здесь или из Интернета, как объяснение Anders Göransson . Я пытаюсь обобщить некоторые другие чтения прямо здесь и быстро объяснить решение, если executeOnExecutor все еще работает в одном потоке ...

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

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

С 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 (который запускает Задачи последовательно в посту Соты).
Если вы не определили targetSdkVersion, тогда minSdkVersion автоматически считается targetSdkVersion.
Следовательно, для параллельного запуска AsyncTask на пост-соте вы не можете оставить targetSdkVersion пустым.

7 голосов
/ 18 января 2016

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

Путь 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 голосов
/ 01 июня 2012

У меня была та же проблема: я не могу выполнить второй AsyncTask после того, как я вызвал «execute» для первого: 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);
}

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

5 голосов
/ 03 февраля 2012

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

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

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

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

David

2 голосов
/ 28 сентября 2013

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

В любом случае, замена AsyncTask на запасную почти упала. Вы можете даже назвать его AsyncTask, если хотите, но чтобы избежать путаницы, он называется ThreadedAsyncTask. Вам нужно вызвать executeStart () вместо execute, потому что 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); 
    }
}
1 голос
/ 03 мая 2013

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

0 голосов
/ 15 мая 2014

На основании ответа Матье, ниже класса helper для правильного выполнения 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);
0 голосов
/ 25 февраля 2012

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...