web-dev-qa-db-fra.com

AsyncTask SDK Android doInBackground non en cours d'exécution (sous-classe)

Au 15/2/2012, je n'ai pas encore trouvé d'explication valable ni de raison pour laquelle cela ne fonctionne pas. La solution la plus proche est d'utiliser l'approche traditionnelle Thread , mais pourquoi alors inclure une classe qui ne semble pas (fonctionner) dans le SDK Android?

Même si!

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

C'est exécuté comme ça:

xmlAsync xmlThread = new xmlAsync();

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

Maintenant, cette sous-classe a rencontré une petite erreur. Auparavant, il effectuait quelques analyses xml, mais quand j'ai remarqué que c'est doInBackground () n'a pas été appelé, je l'ai réduit, ligne par ligne, pour finalement aboutir à ceci:

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

Qui, pour une raison quelconque, n'a consigné rien. 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 onPreExecute () est appelé, mais pas doInBackground ()} _. J'ai une autre tâche Async fonctionnant en arrière-plan en même temps, ce qui fonctionne très bien.

J'utilise actuellement l'application sur un émulateur, SDK version 15, Eclipse, Mac OS X 10.7.2, proche 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 de données SQLite, initialisée avec le contexte de l'activité. publishProgress () est appelé par le rappel de l'interface ParseListener. Cependant, comme je ne fais même rien sauf log.v dans doInBackground (), j'ai d'abord trouvé inutile de le rappeler.

EDIT 2:

Bon, pour être parfaitement clair, voici l’autre tâche Async, exécutant la même activité et fonctionnant parfaitement.

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:

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

La solution de Matthieu fonctionnera bien pour la plupart, mais certains peuvent faire face à des problèmes; À moins de creuser dans de nombreux liens fournis ici ou sur le Web, tels que l'explication de Anders Göransson .

Le comportement de AsyncTask().execute(); a changé avec les versions Android. Avant Donut _ ​​(Android: 1.6 API: 4) les tâches ont été exécutées en série, de Donut à Gingerbread _ ​​(Android: 2.3 API: 9) tâches exécutées en parallèle; depuis Honeycomb _ ​​(Android: 3.0 API: 11) l'exécution a été basculée en mode séquentiel; 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 avez besoin que les tâches soient traitées en parallèle dans des threads distincts.

Avec AsyncTask, l'exécution en série n'est pas disponible entre les versions de Donut et Honeycomb, tandis que l'exécution en parallèle n'est pas disponible avant Donut.

Pour un traitement parallèle après Donut: Vérifiez la version de Build et utilisez la méthode .execute () ou .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 .executeOnExecutor() vérifie si targetSdkVersion du projet est inférieur ou égal à HONEYCOMB_MR1 (Android: 2.1 API: 7), puis oblige l'exécuteur à utiliser THREAD_POOL_EXECUTOR (qui exécute les tâches de manière séquentielle dans Honeycomb).
Si vous n'avez pas défini une targetSdkVersion, alors minSdkVersion est automatiquement considéré comme étant la targetSdkVersion.
Par conséquent, pour exécuter votre tâche asynchrone en parallèle sur le post Honeycomb, vous ne pouvez pas laisser targetSdkVersion vide.

105
Nashe

Vous devriez vérifier cette réponse: https://stackoverflow.com/a/10406894/347565 et le lien vers les groupes de Google inclus.

J'avais un problème similaire au vôtre, je ne comprenais toujours pas pourquoi cela ne fonctionnait pas, mais j'ai changé mon code comme ceci 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);
156
Matthieu

J'ai eu le même problème: je ne peux pas exécuter une seconde AsyncTask après avoir appelé "execute" sur une première: doInBackground n'est appelé que pour la première.

Pour répondre à cette question, cochez answer (comportement différent selon le SDK) 

Cependant, dans votre cas, cet obstacle peut être évité en utilisant executeOnExecutor (disponible à partir de la version 3.0 fonctionnant pour moi avec la version 4.0.3), mais méfiez-vous des limites de la taille et de la mise en file d'attente du pool de threads.

Pouvez-vous essayer quelque chose comme ça:

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: cela est expliqué dans la -docs En gros, juste pour éviter tous les problèmes qui pourraient provenir du multithreading comme les interférences ...

6
code7amza

Vous pouvez le faire de deux manières:

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

En cas de voie 1 ne fonctionne pas pour vous essayez voie 2 .

Voie 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 ceci vous aidera.

5
Hiren Patel

Une chose que j'aimerais savoir et qui pourrait résoudre votre problème est de savoir où installez-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 d'interface utilisateur principal. Si vous créez votre objet et appelez l'exécutable à partir d'un autre thread, onPreExecute risque de se déclencher. Je ne suis pas certain à 100% ici, mais le thread d'arrière-plan ne sera ni créé ni exécuté.

Si vous créez l'instance de votre AsyncTask à partir d'un thread en arrière-plan ou si une autre opération n'a pas lieu sur le thread principal de l'interface utilisateur, vous pouvez envisager d'utiliser la méthode suivante: Activity.runOnUiThread (Runnable)

Vous aurez 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 d'interface utilisateur à partir d'un autre code qui ne s'exécute pas sur le thread d'interface utilisateur.

J'espère que cela a du sens. Faites-moi savoir si je peux aider davantage.

David

5

Android est brutal! Je ne peux pas croire cela, quelle implémentation floconneuse qui change de jour en jour. Un jour, c’est un seul fil, le lendemain, son 5, l’autre est 128.

Quoi qu'il en soit, le remplacement du stock AsyncTask est presque terminé. Vous pouvez même l'appeler AsyncTask si vous le souhaitez, mais pour éviter toute confusion, il est appelé ThreadedAsyncTask. Vous devez appeler executeStart () au lieu d'exécuter, 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); 
    }
}
2
Kevin Parker

Je sais que cela peut être très tard 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 permettait d’en exécuter qu’un à la fois, puis, un peu plus tard, je ne savais pas quelle version, ils vous permettaient d’exécuter simultanément plusieurs asynctoniques, ce qui posait problème dans de nombreuses applications permettant à un asynctask de s'exécuter à la fois. Sauf si vous modifiez manuellement le pool de threads . Nous espérons que cela efface un ou deux points pour les utilisateurs.

Selon la réponse de Matthieu, sous la classe helper, vous exécuterez votre AsyncTask en fonction de la version du SDK afin d'éviter de dupliquer le 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);
0
L. G.

je pense que c'est le SDK. J'ai eu le même problème, et après avoir changé la cible SDK de 15 à 11, tout fonctionne parfaitement.

avec sdk15, même si AsyncTask.Status est en cours d'exécution, doInBackground n'est jamais appelé. Je pense cependant que cela a quelque chose à voir avec le fil d'interface utilisateur.

0
phobus