Android SDK AsyncTask doInBackground не работает (подкласс)
по состоянию на 15/2/2012 я пока не нашел хорошего объяснения, ни причины, почему это не работает. Наиболее близким к решению является использование традиционного подхода к потокам, но тогда зачем включать класс, который (кажется) не работает в 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 () более или менее добавляет строку в базу данных SQLiteDatabase, инициализированную контекстом действия. publishProgress () вызывается обратным вызовом ParseListener интерфейса. Однако, поскольку я даже не делаю ничего, кроме журнала.v в doInBackground () я впервые нашел об этом излишне даже говорить.
EDIT 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();
}
}
EDIT 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 ответов:
решение Matthieu будет отлично работать для большинства, но некоторые могут столкнуться с проблемой; если не копаться во многих ссылках, предоставленных здесь или из интернета, например Андерс Йоранссонобъяснение. Я пытаюсь суммировать некоторые другие чтения прямо здесь и быстро объяснить решение, если 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.
для параллельной обработки после пончика: проверьте версию сборки и на основе этого использования .казнить или. 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
of проект меньше или равенHONEYCOMB_MR1
(Android:2.1 API:7) тогда он заставляет исполнителя бытьTHREAD_POOL_EXECUTOR
(который запускает задачи последовательно в post Honeycomb).
Если вы не определили atargetSdkVersion
затемminSdkVersion
автоматически считаетсяtargetSdkVersion
.
Поэтому для запуска вашей AsyncTask параллельно на post Honeycomb вы не можете оставитьtargetSdkVersion
пустой.
вы должны проверить этот ответ: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);
У меня была та же проблема : не могу выполнить вторую 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); }
для вашего вопроса обновления : это объясняется в docs В основном, чтобы избежать всех проблем, которые могут возникнуть из многопоточности, таких как intereference ....
одна вещь, которую я хотел бы знать, и это может исправить вашу проблему, где вы инстанцировать экземпляр класса и вызываю метод execute ()? Если Вы читаете документацию для AsyncTask, обе эти операции должны выполняться в основном потоке пользовательского интерфейса. Если вы создаете свой объект и вызываете execute из какого-то другого потока, то onPreExecute может сработать, я не уверен на 100% здесь, но фоновый поток не будет создан и выполненный.
Если вы создаете экземпляр AsyncTask из фонового потока или какой-либо другой операции, не выполняемой в основном потоке пользовательского интерфейса, вы можете рассмотреть возможность использования этого метода: активности.runOnUiThread (Runnable)
для вызова этого метода вам потребуется доступ к экземпляру выполняемой операции, но он позволит вам запускать код в потоке пользовательского интерфейса из другого кода, который не выполняется в потоке пользовательского интерфейса.
надеюсь, что делает чувство. Дайте мне знать, если я могу помочь больше.
Дэвид
Вы можете сделать это двумя способами:
Способ 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);
надеюсь, что это поможет вам.
Android жестоко! Я не могу поверить в это, какая зыбкая реализация, которая меняется со дня на день. Один день его один поток, следующий его 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); } }
Я знаю, что это может быть очень поздно для потока, но есть причина, по которой он не будет работать на более поздних эмуляторах android. Когда asynctask был представлен android, вы можете запускать только по одному, а затем через некоторое время, я не уверен, какая версия,они позволили вам запускать сразу несколько asynctasks, это вызвало проблемы во многих приложениях, и поэтому в Honeycomb+ они вернулись к разрешению только одной asynctask одновременно. Если вы вручную не измените пул потоков. Надеюсь, что проясняет одну или две вещи для людей.
Я думаю, что это sdk. у меня была та же проблема, и после изменения целевого sdk с 15 на 11, все работает отлично.
с sdk15, даже если AsyncTask.Статус выполняется, doInBackground никогда не вызывается. я думаю, что это имеет какое-то отношение к потоку пользовательского интерфейса, хотя.
на основе ответа Матье, ниже помощник класс для выполнения вашего
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);