Na dzień 15/2/2012 nie znalazłem jeszcze dobrego wyjaśnienia ani powodu, dla którego to nie działa. Najbliższym rozwiązaniem jest użycie tradycyjnego podejścia Thread , ale w takim razie po co dołączać klasę, która (wydaje się) nie działać w Android SDK?
Nawet TAK!
Mam podklasę 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
To jest wykonywane w ten sposób:
xmlAsync xmlThread = new xmlAsync();
xmlThread.execute("http://www.nothing.com");
Teraz ta podklasa napotkała mały błąd. Wcześniej przeprowadzał parsowanie xml, ale kiedy zauważyłem, że metoda doInBackground () nie została wywołana , usunąłem ją, linia po linii, ostatecznie kończąc na tym:
@Override
protected Void doInBackground(String... params)
{
Log.v(TAG, "doInBackground");
return null;
}
Który z jakiegoś powodu nic nie zarejestrował. Jednak dodałem to:
@Override
protected void onPreExecute()
{
Log.v(TAG, "onPreExecute");
super.onPreExecute();
}
Ta linia jest rzeczywiście rejestrowana podczas wykonywania wątku. W jakiś sposób wywoływana jest metoda onPreExecute (), ale nie metoda doInBackground () . Mam jednocześnie inne AsyncTask działające w tle, które działa dobrze.
Obecnie używam aplikacji na emulatorze SDK w wersji 15, Eclipse, Mac OS X 10.7.2, blisko bieguna północnego.
EDYTOWAĆ:
@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 () mniej więcej dodaje wiersz do bazy danych SQLiteDatabase, zainicjowany z kontekstem działania. PublishingProgress () jest wywoływana przez wywołanie zwrotne interfejsu ParseListener. Jednakże, ponieważ nie robię nawet nic poza log.v w doInBackground (), po raz pierwszy uznałem to za niepotrzebne nawet do wywoływania.
EDYCJA 2:
W porządku, żeby było jasne, to jest drugie AsyncTask, wykonujące tę samą czynność i działające idealnie.
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();
}
}
EDYCJA 3:
Wzdycham, przepraszam, źle mi zadaje pytania Ale tutaj jest inicjalizacja zadań.
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);
}
Odpowiedzi:
Rozwiązanie Matthieu będzie działać dobrze w większości przypadków, ale niektórzy mogą napotkać problem; chyba że kopie w wielu linkach podanych tutaj lub z sieci, jak wyjaśnienie Andersa Göranssona . Próbuję podsumować kilka innych odczytów tutaj i szybko wyjaśnić rozwiązanie, jeśli executeOnExecutor nadal działa w jednym wątku ...
Zachowanie
AsyncTask().execute();
zmieniło się w wersjach Androida. Przed Donutem (Android: 1.6 API: 4) zadania były wykonywane szeregowo, od Donut do Gingerbread (Android: 2.3 API: 9) zadania wykonywane równolegle; ponieważ wykonanie Honeycomb (Android: 3.0 API: 11) zostało przełączone z powrotem na sekwencyjne; dodanoAsyncTask().executeOnExecutor(Executor)
jednak nową metodę do wykonywania równoległego.W przetwarzaniu sekwencyjnym wszystkie zadania Async są uruchamiane w jednym wątku i dlatego muszą czekać na zakończenie poprzedniego zadania. Jeśli chcesz wykonać kod natychmiast, potrzebujesz, aby zadania były przetwarzane równolegle w oddzielnych wątkach.
W przypadku AsyncTask seryjne wykonywanie nie jest dostępne między wersjami Donut i Honeycomb, podczas gdy wykonywanie równoległe nie jest dostępne przed Donut.
W przypadku przetwarzania równoległego po Donut: sprawdź wersję kompilacji i na jej podstawie użyj metody .execute () lub .executeOnExecutor (). Poniższy kod może pomóc ...
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:
Funkcja.executeOnExecutor()
sprawdza, czytargetSdkVersion
projekt jest mniejszy lub równyHONEYCOMB_MR1
(Android: 2.1 API: 7), a następnie wymusza wykonanie programuTHREAD_POOL_EXECUTOR
(który uruchamia zadania sekwencyjnie w post Honeycomb).Jeśli nie zdefiniowałeś a,
targetSdkVersion
tominSdkVersion
jest automatycznie traktowane jakotargetSdkVersion
.Dlatego do równoległego uruchamiania AsyncTask na post Honeycomb nie można pozostawić
targetSdkVersion
pustego.źródło
Powinieneś sprawdzić tę odpowiedź: https://stackoverflow.com/a/10406894/347565 i link do grup google, które zawiera.
Miałem podobny problem jak Ty, nadal nie wiadomo, dlaczego nie działa, ale zmieniłem kod w ten sposób i problem zniknął:
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);
źródło
Możesz to zrobić na dwa sposoby:
Sposób 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(); }
W przypadku, gdy sposób 1 nie działa, wypróbuj sposób 2 .
Sposób 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);
Mam nadzieję, że to ci pomoże.
źródło
Miałem ten sam problem: nie mogę wykonać drugiego AsyncTask po wywołaniu „execute” na pierwszym: funkcja doInBackground jest wywoływana tylko dla pierwszego.
Aby odpowiedzieć, dlaczego tak się dzieje, zaznacz tę odpowiedź (różne zachowanie w zależności od SDK)
Jednak w twoim przypadku tę przeszkodę można ominąć, używając executeOnExecutor (dostępne od wersji 3.0 działało dla mnie przy użyciu 4.0.3), ale uważaj na ograniczenia rozmiaru puli wątków i kolejkowania.
Czy możesz spróbować czegoś takiego:
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); }
W przypadku pytania o aktualizację: zostało to wyjaśnione w dokumentach Zasadniczo, aby uniknąć wszystkich problemów, które mogą wynikać z wielowątkowości, takich jak interferencja ...
źródło
Jedną rzeczą, którą chciałbym wiedzieć i która może faktycznie rozwiązać Twój problem, jest to, gdzie tworzysz wystąpienie swojej klasy i wywołujesz metodę execute ()? Jeśli przeczytasz dokumentację AsyncTask, obie te operacje muszą zostać wykonane w głównym wątku interfejsu użytkownika. Jeśli tworzysz obiekt i wywołujesz execute z innego wątku, to onPreExecute może odpalić, nie jestem tutaj w 100% pewien, ale wątek w tle nie zostanie utworzony i wykonany.
Jeśli tworzysz wystąpienie swojego AsyncTask z wątku w tle lub inną operację, która nie jest wykonywana w głównym wątku interfejsu użytkownika, możesz rozważyć użycie metody: Activity.runOnUiThread (Runnable)
Aby wywołać tę metodę, potrzebujesz dostępu do wystąpienia uruchomionego działania, ale umożliwi to uruchomienie kodu w wątku interfejsu użytkownika z innego kodu, który nie jest uruchomiony w wątku interfejsu użytkownika.
Mam nadzieję, że to ma sens. Daj mi znać, jeśli mogę więcej pomóc.
David
źródło
Android jest brutalny! Nie mogę w to uwierzyć, co za krucha implementacja, która zmienia się z dnia na dzień. Jednego dnia to pojedynczy wątek, następnego jego 5, drugi to 128.
W każdym razie tutaj jest prawie spadek wymiany zapasowego AsyncTask. Możesz nawet nazwać go AsyncTask, jeśli chcesz, ale aby uniknąć nieporozumień, nazywa się go ThreadedAsyncTask. Musisz wywołać executeStart () zamiast execute, ponieważ execute () jest ostateczna.
/** * @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); } }
źródło
Wiem, że może to być naprawdę spóźniony wątek, ale jest powód, dla którego nie będzie działać na późniejszych emulatorach Androida. Kiedy asynctask został wprowadzony, android pozwolił ci uruchamiać tylko jedno na raz, a później, nie jestem pewien, która wersja, pozwoliła ci uruchomić wiele asynctask naraz, spowodowało to problemy w wielu aplikacjach, więc w Honeycomb + powrócili do tylko pozwalając na wykonanie jednego zadania asynchronicznego naraz. Chyba że ręcznie zmienisz pulę wątków. Mam nadzieję, że to wyjaśnia jedną lub dwie rzeczy dla ludzi.
źródło
myślę, że to sdk. miałem ten sam problem i po zmianie docelowego sdk z 15 na 11 wszystko działa idealnie.
z sdk15, mimo że AsyncTask.Status jest RUNNING, metoda doInBackground nigdy nie jest wywoływana. myślę, że ma to coś wspólnego z wątkiem interfejsu użytkownika.
źródło
Na podstawie odpowiedzi Matthieu, poniżej klasy pomocniczej do
AsyncTask
prawidłowego wykonania w zależności od wersji SDK, aby uniknąć powielania kodu w aplikacji: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); } } }
Przykład użycia:
public class MyTask extends AsyncTask<Void, Void, List<String>> {
...
final MyTask myTask = new MyTask(); new AsyncTaskExecutor<Void, Void, List<String>>().execute(myTask);
źródło