Android SDK AsyncTask doInBackground non in esecuzione (sottoclasse)


90

A partire dal 15/2/2012 non ho ancora trovato una buona spiegazione né un motivo per cui questo non funziona. Il più vicino a una soluzione è utilizzare l' approccio Thread tradizionale , ma allora perché includere una classe che non (sembra) funziona nell'SDK Android?

Anche così!

Ho una sottoclasse 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

Viene eseguito in questo modo:

xmlAsync xmlThread = new xmlAsync();

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

Ora questa sottoclasse ha riscontrato un piccolo errore. In precedenza ha eseguito un po 'di analisi xml, ma quando ho notato che doInBackground () non si chiamava, l'ho smontato, riga per riga, finendo infine con questo:

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

Che, per qualche motivo, non ha registrato nulla. Tuttavia, ho aggiunto questo:

@Override
protected void onPreExecute() 
{
        Log.v(TAG, "onPreExecute");
        super.onPreExecute();
}

E quella riga viene effettivamente registrata durante l'esecuzione del thread. Quindi in qualche modo viene chiamato onPreExecute () ma non doInBackground () . Ho un altro AsyncTask in esecuzione in background allo stesso tempo che funziona perfettamente.

Attualmente sto eseguendo l'app su un emulatore, SDK versione 15, Eclipse, Mac OS X 10.7.2, vicino al Polo Nord.

MODIFICARE:

@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 () aggiunge più o meno una riga a un SQLiteDatabase, inizializzato con il contesto dell'attività. publishProgress () viene chiamato dal callback di Interface ParseListener. Tuttavia, poiché non faccio nemmeno nulla tranne log.v in doInBackground (), per prima cosa ho trovato questo non necessario nemmeno per farlo apparire.

MODIFICA 2:

Va bene, solo per essere perfettamente chiari, questo è l'altro AsyncTask, che viene eseguito nella stessa attività e funziona perfettamente.

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

MODIFICA 3:

Sigh, scusa, non sono bravo a fare domande. Ma ecco l'inizializzazione delle attività.

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

è possibile che l'attività finisca prima del completamento dell'attività?
Paul Nikonowicz

Altamente improbabile. In tal caso il mio altro thread non funzionerebbe, giusto? E al momento ho solo un'attività.
SeruK

2
La mia app ha lo stesso problema: doInBackground non viene chiamato o viene chiamato con un ritardo molto lungo. Ecco la mia osservazione limitata: esattamente lo stesso codice funziona perfettamente su uno smartphone Android 2.3.3 e un emulatore Android 2.3.3, ma ha questo problema su un tablet Android 4.0.3 e un mucchio di emulatori Android 4.xx. È molto allettante concludere che questo problema sia stato introdotto nelle versioni più recenti di Android.
Hong

Mi dispiace, ma ho dimenticato di menzionare questo problema si verifica solo con il secondo AsyncTask di un'attività Il primo AsyncTask funziona sempre bene.
Hong

Hong, hai provato la risposta di Matthieu? Sono per lo più fuori dal bancomat del gioco Android e non ci ho lavorato per un po ', quindi non posso dire se le sue risposte funzionano davvero. Se non è per te, forse è stato un male da parte mia accettare la sua risposta ...
SeruK

Risposte:


107

La soluzione di Matthieu funzionerà bene per la maggior parte, ma alcuni possono affrontare problemi; a meno che non si scavano molti collegamenti forniti qui o dal web, come la spiegazione di Anders Göransson . Sto cercando di riassumere alcune altre letture proprio qui e spiegare rapidamente la soluzione se executeOnExecutor funziona ancora in un singolo thread ...

Il comportamento di AsyncTask().execute();è cambiato nelle versioni di Android. Prima di Donut (Android: 1.6 API: 4) le attività venivano eseguite in serie, da Donut a Gingerbread (Android: 2.3 API: 9) le attività venivano eseguite in parallelo; poiché l' esecuzione di Honeycomb (Android: 3.0 API: 11) è stata ripristinata in sequenza; AsyncTask().executeOnExecutor(Executor)tuttavia, è stato aggiunto un nuovo metodo per l'esecuzione parallela.

Nell'elaborazione sequenziale tutte le attività asincrone vengono eseguite in un singolo thread e quindi devono attendere prima che l'attività precedente termini. Se è necessario eseguire immediatamente il codice, è necessario che le attività vengano elaborate in parallelo in thread separati.

Con AsyncTask l'esecuzione seriale non è disponibile tra le versioni Donut e Honeycomb, mentre l'esecuzione parallela non è disponibile prima di Donut.

Per l'elaborazione parallela dopo Donut: controlla la versione Build e in base a quella utilizza il metodo .execute () o .executeOnExecutor (). Il codice seguente può aiutare ...

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 funzione .executeOnExecutor()controlla se il targetSdkVersionprogetto è minore o uguale a HONEYCOMB_MR1(Android: 2.1 API: 7) quindi forza l'esecutore a essere THREAD_POOL_EXECUTOR(che esegue Tasks in sequenza in post Honeycomb).
Se non hai definito un, targetSdkVersionallora minSdkVersionviene automaticamente considerato come targetSdkVersion.
Quindi per eseguire il tuo AsyncTask in parallelo su post Honeycomb non puoi lasciare targetSdkVersionvuoto.


1
Risposta molto buona. Sebbene Matthieu's non abbia torto, lo accetto, dal momento che aggiungi un sacco di informazioni importanti.
SeruK

@Nashe Grazie mille. È davvero molto utile. Ho combattuto con lo stesso problema per 3 giorni. Grazie ancora :)

Mi ha salvato la giornata! Vorrei che questa risposta fosse più facile da trovare.
zjk

4
Ehi @Nashe, il mio problema è un po 'imbarazzante. Prima di oggi stavo usando il metodo .execute () su AsyncTask e il codice funzionava perfettamente. Ma oggi ho riscontrato il problema: il controllo non va nel metodo doInBackground (). Sebbene la soluzione che fornisci funzioni, sono perplesso come funzionasse prima senza la soluzione. Sto usando lo stesso set di dispositivi prima e ora.
Ravi Sisodia

Perché dovrebbe essere così? Perché non funziona secondo le aspettative? :(
Nikolay R

160

Dovresti controllare questa risposta: https://stackoverflow.com/a/10406894/347565 e il link ai gruppi di Google che include.

Ho avuto un problema simile al tuo, ancora poco chiaro perché non funziona, ma ho cambiato il mio codice in questo modo e il problema è sparito:

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);

Sono stato molto male nel guardare indietro a questo post; Da tempo mi sono allontanato dal progetto. Accetterò semplicemente questa come risposta poiché le persone sembrano dire che funziona.
SeruK

questo ha funzionato per me solo ora, ma vorrei aver capito perché. stava funzionando bene con l'esecuzione prima che si fermasse. una cosa che sto facendo è avviare un nuovo asynctask all'interno di onPostExecute dello stesso asynctask (cioè lo sto chiamando in modo ricorsivo) forse è collegato al problema?
steveh

Penso che questo dovrebbe darti tutte le spiegazioni di cui hai bisogno: commonsware.com/blog/2012/04/20/…
Matthieu

1
@ Matthieu: Signore, non posso davvero ringraziarti abbastanza !!! Ho battuto la testa su questo problema per ore e la tua soluzione ha fatto funzionare tutto a meraviglia! Grazie mille per la fantastica risposta. Vorrei poter dare più di un voto positivo !!
Swayam


9

Puoi farlo in due modi:

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

Nel caso in cui il modo 1 non funziona, prova il modo 2 .

Modo 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);

Spero che questo ti possa aiutare.


super amico funziona, ma le prossime attività Async successive che utilizzano AsyncTask.THREAD_POOL_EXECUTOR non sono riuscite, quindi ho bisogno di cambiare con CustomExecutor in tutto il progetto immagino :(
kumar

@vinu, ti suggerisco di utilizzare un'attività asincrona comune e un metodo comune per eseguire AsyncTask. Spero che questo ti possa aiutare.
Hiren Patel

2
Ehi, questo mi ha aiutato molto! Grazie!
Justin Ebby

6

Ho avuto lo stesso problema: non è possibile eseguire un secondo AsyncTask dopo aver chiamato "execute" su un primo: doInBackground viene chiamato solo per il primo.

Per rispondere al motivo di ciò, controlla questa risposta (comportamento diverso a seconda dell'SDK)

Tuttavia, per il tuo caso, questo ostacolo può essere evitato usando executeOnExecutor (disponibile a partire da 3.0 ha funzionato per me usando 4.0.3) ma fai attenzione alle limitazioni della dimensione del pool di thread e dell'accodamento.

Puoi provare qualcosa di simile:

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

Per la tua domanda di aggiornamento: è spiegato nei documenti Fondamentalmente solo per evitare tutti i problemi che possono derivare dal multithreading come l'interferenza ....


5

Una cosa che vorrei sapere, e potrebbe effettivamente risolvere il tuo problema, è dove stai istanziando l'istanza della tua classe e chiamando il metodo execute ()? Se leggi la documentazione per AsyncTask, entrambe le operazioni devono essere eseguite sul thread dell'interfaccia utente principale. Se stai creando il tuo oggetto e chiamando execute da qualche altro thread, onPreExecute potrebbe attivarsi, non ne sono sicuro al 100% qui, ma il thread in background non verrà creato ed eseguito.

Se stai creando l'istanza del tuo AsyncTask da un thread in background, o qualche altra operazione che non avviene sul thread principale dell'interfaccia utente, potresti prendere in considerazione l'utilizzo del metodo: Activity.runOnUiThread (Runnable)

Avresti bisogno dell'accesso a un'istanza della tua attività in esecuzione per chiamare quel metodo, ma ti consentirà di eseguire codice sul thread dell'interfaccia utente da un altro codice che non è in esecuzione sul thread dell'interfaccia utente.

Spero che abbia un senso. Fammi sapere se posso aiutarti di più.

David


aggiungendo alla tua risposta questo thread ha una risposta interessante stackoverflow.com/questions/4080808/… .
manjusg

Grazie per la magnifica risposta! L'ho aumentato perché penso che potrebbe essere un problema comune per principianti di AsyncTask. Purtroppo, non è proprio la risposta giusta per questo problema. Entrambe le classi vengono istanziate in onCreate () di un'attività in esecuzione sul thread dell'interfaccia utente principale. Ho solo un'attività in questo progetto.
SeruK

@manjusg Ho sempre considerato che aveva qualcosa a che fare con AsyncTask che era instabile, forse molto di più quando diversi vengono eseguiti contemporaneamente. In caso affermativo, perché?
SeruK

Non so davvero quali politiche SO abbia riguardo al postare rapidamente tre volte di seguito, ma l'ho trovato nell'altro thread ... foo.jasonhudgins.com/2010/05/limitations-of-asynctask.html "AsyncTask utilizza una coda di lavoro interna statica con un limite hardcoded di 10 elementi. " Questo potrebbe provare qualcosa, ma ho solo due istanze di sottoclassi AsyncTask! Vorrei davvero evitare di utilizzare i normali metodi di threading poiché alla fine ci sarà un sacco di parsing fatto in dere.
SeruK

2

Android è brutale! Non posso crederci, quale implementazione instabile che cambia di giorno in giorno. Un giorno è un singolo thread, il prossimo è 5 l'altro è 128.

Comunque qui è quasi un calo in sostituzione dell'AsyncTask di serie. Puoi anche chiamarlo AsyncTask se lo desideri, ma per evitare confusione è chiamato ThreadedAsyncTask. Devi chiamare executeStart () invece di execute perché execute () è definitivo.

/**
 * @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); 
    }
}

Aspetta, non stai dicendo che alcune versioni di Android limitano le operazioni asincrone a un thread, vero? Sarebbe incredibilmente sciocco. (Sono stato fuori dal gioco per Android per un po '. :))
SeruK

Sì, su Android 3.0+ se non usi AsyncTask.THREAD_POOL_EXECUTOR ottieni solo un pool di thread di uno. Provalo per te stesso, due AsyncTask e dormi nel proprio doInBackground. Dalla documentazione di Android AsyncTask: "A partire da HONEYCOMB, le attività vengono eseguite su un singolo thread per evitare errori comuni dell'applicazione causati dall'esecuzione parallela."
Kevin Parker

1

So che potrebbe essere davvero tardi per il thread, ma c'è un motivo per cui non funzionerà sugli emulatori Android successivi. Quando è stato introdotto asynctask, Android ti permetteva di eseguirne solo uno alla volta, poi qualche tempo dopo, non sono sicuro di quale versione, ti hanno permesso di eseguire più asynctask contemporaneamente, questo ha causato problemi in molte app, e quindi in Honeycomb + sono tornati solo consentendo l'esecuzione di un'attività asincrona alla volta. A meno che non si modifichi manualmente il pool di thread. Spero che questo chiarisca una o due cose per le persone.


0

Penso che sia l'SDK. ho avuto lo stesso problema, e dopo aver cambiato target sdk da 15 a 11, tutto funziona perfettamente.

con sdk15, anche se AsyncTask.Status è IN ESECUZIONE, doInBackground non viene mai chiamato. Penso che abbia qualcosa a che fare con il thread dell'interfaccia utente però.


Non posso né negare né confermare perché non ho davvero il tempo in questo momento per provarlo. Tutto quello che posso dire è che stavo usando l'SDK 15, quindi è molto probabile.
SeruK

0

Sulla base della risposta di Matthieu, di seguito una classe helper per eseguire AsyncTaskcorrettamente la tua a seconda della versione dell'SDK per evitare di duplicare il codice nella tua applicazione:

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

}

Esempio di utilizzo:

public class MyTask extends AsyncTask<Void, Void, List<String>> {

...

final MyTask myTask = new MyTask();
new AsyncTaskExecutor<Void, Void, List<String>>().execute(myTask);
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.