Android SDK AsyncTask doInBackground ne fonctionne pas (sous-classe)
Comme de 15/2/2012 je n'ai pas encore trouvé une bonne explication, ni une raison pour laquelle cela ne fonctionne pas. Le plus proche d'une solution est d'utiliser l'approche traditionnelle Thread, mais alors pourquoi inclure une classe qui ne fonctionne pas (semble) dans le SDK Android?
Encoredans' ALORS!
J'ai une sous-classe 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
Qui est exécuté comme ceci:
xmlAsync xmlThread = new xmlAsync();
xmlThread.execute("http://www.nothing.com");
Maintenant, cette sous-classe a rencontré une petite erreur. Auparavant, il faisait de l'analyse xml, mais quand je remarqué que c'est doInBackground () n'a pas été appelé je l'ai dépouillé, ligne par ligne, se terminant finalement avec juste ceci:
@Override
protected Void doInBackground(String... params)
{
Log.v(TAG, "doInBackground");
return null;
}
Qui, pour une raison quelconque, n'a rien enregistré. Cependant, j'ai ajouté ceci:
@Override
protected void onPreExecute()
{
Log.v(TAG, "onPreExecute");
super.onPreExecute();
}
Et cette ligne est en effet enregistrée lors de l'exécution du thread. donc, en quelque sorte onPreExecute () est appelé mais pas doInBackground(). J'ai une autre AsyncTask en arrière-plan en même temps qui fonctionne très bien.
Je cours actuellement l'application sur un émulateur, SDK Version 15, Eclipse, Mac OS X 10.7.2, près du pôle Nord.
Modifier:
@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 () ajoute plus ou moins une ligne à une base SQLiteDatabase, initialisée avec le contexte de l'activité. publishProgress() est appelée par le rappel de L'Interface ParseListener. Cependant, puisque je ne fais même rien sauf le journal.v dans doInBackground () j'ai d'abord trouvé cela inutile même pour élever.
MODIFIER 2:
Très bien, juste pour être parfaitement clair, c'est L'autre AsyncTask, s'exécutant dans la même activité et fonctionnant parfaitement bien.
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();
}
}
MODIFIER 3:
Soupir, désolé je suis mauvais pour poser des questions. Mais voici l'initialisation des tâches.
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 réponses
La solution de Matthieu fonctionnera bien pour la plupart, mais certains peuvent faire face à un problème; à moins de creuser dans de nombreux liens fournis ici ou à partir du web, comme Anders Göransson l'explication. J'essaie de résumer d'autres lectures ici et d'expliquer rapidement la solution si executeOnExecutor fonctionne toujours dans un seul thread...
Le comportement de AsyncTask().execute();
a changé via les versions Android. Avant Donut (Android: 1.6 API: 4) les tâches ont été exécutées en série, à partir de beignet à pain D'épice (Android: 2.3 API: 9) tâches exécutées en parallèle; depuis nid D'abeille (Android: 3.0 API: 11) l'exécution a été ramenée à sequential; une nouvelle méthode AsyncTask().executeOnExecutor(Executor)
a cependant été ajoutée pour une exécution parallèle.
Dans le traitement séquentiel, toutes les tâches asynchrones s'exécutent dans un seul thread et doivent donc attendre avant la fin de la tâche précédente. Si vous devez exécuter du code immédiatement, vous devez traiter les tâches en parallèle séparément threads.
Avec AsyncTask, l'exécution série n'est pas disponible entre les versions Donut et Honeycomb, tandis que l'exécution parallèle n'est pas disponible avant Donut.
Pour le traitement parallèle après Donut: Vérifiez la version de construction et en fonction de cette utilisation .execute() ou .méthode executeOnExecutor (). Le code suivant peut aider...
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:
la fonction {[4] } vérifie si targetSdkVersion
du projet est inférieur ou égal à HONEYCOMB_MR1
(Android: 2.1 API: 7) alors elle force l'exécuteur à être THREAD_POOL_EXECUTOR
(qui exécute les tâches séquentiellement dans Post Honeycomb).
Si vous n'avez pas défini un targetSdkVersion
, puis minSdkVersion
, est automatiquement considéré comme le targetSdkVersion
.
Par conséquent, pour exécuter votre AsyncTask en parallèle sur Post Honeycomb, vous ne pouvez pas laisser targetSdkVersion
vide.
Vous devriez vérifier cette réponse: https://stackoverflow.com/a/10406894/347565 et le lien vers Google groups qu'il inclut.
J'ai eu un problème similaire à vous, Je ne sais toujours pas pourquoi cela ne fonctionne pas, mais j'ai changé mon code comme ça et le problème a disparu:
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);
J'ai eu le même problème: ne peut pas exécuter une seconde AsyncTask après avoir appelé "execute" sur un premier: doInBackground n'est appelé que pour le premier.
Pour répondre à la raison pour laquelle cela se produit, vérifiez cette réponse (comportement différent selon le SDK)
Cependant, pour votre cas, cet obstacle peut être évité en utilisant executeOnExecutor (disponible à partir de 3.0 travaillé pour moi en utilisant 4.0.3) mais méfiez-vous des limitations de la taille du pool de threads et de la file d'attente.
Pouvez-vous essayer quelque chose comme ceci :
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);
}
Pour votre question de mise à jour: elle est expliquée dans les documents Fondamentalement, juste pour éviter tous les problèmes qui peuvent provenir de multithreading comme intereference ....
Une chose que je voudrais savoir, et cela pourrait réellement résoudre votre problème, est où instanciez-vous l'instance de votre classe et appelez la méthode execute ()? Si vous lisez la documentation pour AsyncTask, ces deux opérations doivent avoir lieu sur le thread principal de l'interface utilisateur. Si vous créez votre objet et appelez execute à partir d'un autre thread, onPreExecute peut se déclencher, Je ne suis pas sûr à 100% ici, mais le thread d'arrière-plan ne sera pas créé et exécuté.
Si vous créez l'instance de votre AsyncTask à partir d'un thread d'arrière-plan, ou d'une autre opération qui n'a pas lieu sur le thread principal de L'interface utilisateur, vous pouvez envisager d'utiliser la méthode: activité.runOnUiThread(Runnable)
Vous auriez besoin d'accéder à une instance de votre activité en cours d'exécution pour appeler cette méthode, mais cela vous permettra d'exécuter du code sur le thread de L'interface utilisateur à partir d'un autre code qui ne s'exécute pas sur le thread de L'interface utilisateur.
J'espère que c'est logique. Permettez-moi de savoir si je peux aider plus.
David
Vous pouvez le faire de deux manières:
Façon 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();
}
Dans le cas de façon 1 ne fonctionne pas pour vous essayez façon 2 .
Façon 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);
J'espère que cela vous aidera.
Android est Brutal! Je ne peux pas croire cela, quelle implémentation flakey qui change de jour en jour. Un jour c'est un seul fil, le prochain c'est 5 l'autre est 128.
De toute façon, voici une baisse de remplacement pour le stock AsyncTask. Vous pouvez même l'appeler AsyncTask si vous le souhaitez, mais pour éviter toute confusion, il s'appelle ThreadedAsyncTask. Vous devez appeler executeStart () au lieu de execute car execute () est 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);
}
}
Je sais que cela peut être vraiment en retard pour le fil, mais il y a une raison pour laquelle cela ne fonctionnera pas sur les émulateurs android ultérieurs. Lorsque asynctask a été introduit android ne vous permet d'exécuter un à la fois, puis quelque temps plus tard, je ne sais pas quelle version, ils vous ont permis d'exécuter plusieurs asynctasks à la fois, cela a causé des problèmes dans beaucoup d'applications,et ainsi de suite. Sauf si vous modifiez manuellement le pool de threads. J'espère que efface une ou deux choses pour gens.
Je pense que c'est le sdk. j'ai eu le même problème, et après avoir changé target sdk de 15 à 11, Tout fonctionne parfaitement.
Avec sdk15, même si L'AsyncTask.Le statut est en cours d'exécution, le doInBackground n'est jamais appelé. je pense que cela a quelque chose à voir avec le thread de l'interface utilisateur.
Basé sur la réponse de Matthieu, ci-dessous une classe helper pour exécuter votre AsyncTask
correctement en fonction de la version du SDK afin d'éviter de dupliquer du code dans votre 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);
}
}
}
Exemple d'utilisation:
public class MyTask extends AsyncTask<Void, Void, List<String>> {
...
final MyTask myTask = new MyTask();
new AsyncTaskExecutor<Void, Void, List<String>>().execute(myTask);