2012-02-02 23 views
88

A partire dal 15/2/2012 non ho ancora trovato una buona spiegazione né un motivo per cui ciò non funzioni. La soluzione più vicina a una soluzione è utilizzare l'approccio tradizionale Thread, ma perché includere una classe che non funziona (sembra) nell'SDK di Android?Android SDK AsyncTask doInBackground non in esecuzione (sottoclasse)

Evenin 'SO!

Ho un 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 

che viene eseguito in questo modo:

xmlAsync xmlThread = new xmlAsync(); 

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

Ora, questa sottoclasse ha incontrato un piccolo errore. In precedenza ha fatto un po 'di xml-analisi, ma quando ho notato che è doInBackground() non è stato chiamato ho messo a nudo il basso, riga per riga, infine, termina con solo questo:

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

Il che, per un po' motivo, non ha registrato nulla. Tuttavia, ho aggiunto questo:

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

E quella riga è effettivamente registrata quando si esegue il thread. Quindi in qualche modo onPreExecute() viene chiamato ma non doInBackground(). Ho un altro AsyncTask in esecuzione in background, allo stesso tempo, che funziona bene.

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

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() più o meno aggiunge una riga a una SQLiteDatabase, inizializzato con contesto dell'attività. publishProgress() viene chiamato dal callback dell'interfaccia ParseListener. Tuttavia, dal momento che non faccio nient'altro che log.v in doInBackground(), ho scoperto che non era necessario nemmeno farlo apparire.

EDIT 2:

Va bene, solo per essere perfettamente chiaro, questo è l'altro AsyncTask, eseguendo la stessa attività e perfettamente funzionante soddisfacente.

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:

Sigh, scusate sto male a fare domande. Ma ecco l'inizializzazione dei compiti.

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

è possibile che l'attività termini prima che l'attività venga completata? –

+0

Altamente improbabile. In tal caso il mio altro thread non funzionerebbe correttamente? E ho solo un'attività in questo momento. – SeruK

+2

La mia app ha lo stesso problema: doInBackground non viene chiamato o 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 presenta questo problema su un tablet Android 4.0.3 e un mucchio di emulatori Android 4.x.x. È molto allettante concludere che questo problema è stato introdotto nelle versioni più recenti di Android. – Hong

risposta

104

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

Il comportamento di AsyncTask().execute(); è cambiato tramite le versioni di Android. Prima Donut(Android: 1.6 API: 4) compiti sono stati eseguiti in serie, da Donut a Gingerbread(Android: 2.3 API: 9) prestazioni eseguite in parallelo; dal Honeycomb(Android: 3.0 API: 11) l'esecuzione è stata ripristinata in sequenza; tuttavia, è stato aggiunto un nuovo metodo AsyncTask().executeOnExecutor(Executor) per l'esecuzione parallela.

Nell'elaborazione sequenziale tutte le attività Async 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: controllare la versione Build e in base a tale metodo .execute() o .executeOnExecutor(). A seguito di codice 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: Funzione .executeOnExecutor() ha verificato se targetSdkVersion del progetto è inferiore o uguale a HONEYCOMB_MR1 (Android: 2.1 API: 7) allora costringe l'esecutore di essere THREAD_POOL_EXECUTOR (che esegue le attività sequenzialmente in post Honeycomb).
Se non è stato definito un targetSdkVersion, allora minSdkVersion viene automaticamente considerato come targetSdkVersion.
Quindi per eseguire AsyncTask in parallelo sul post Honeycomb non è possibile lasciare vuoto targetSdkVersion.

+1

Ottima risposta. Mentre Matthieu's non ha torto, lo accetto, dal momento che aggiungi una serie di informazioni importanti. – SeruK

+0

@Nashe Grazie mille. È davvero molto utile Stavo combattendo con questo stesso problema per 3 giorni. Grazie ancora :) –

+1

dopo due giorni finalmente sta funzionando ... –

5

Una cosa che vorrei sapere, e potrebbe effettivamente risolvere il problema, è dove stai istanziare l'istanza della classe e chiamare il metodo execute()? Se si legge la documentazione per AsyncTask, entrambe queste operazioni devono essere eseguite sul thread dell'interfaccia utente principale. Se stai creando il tuo oggetto e la chiamata viene eseguita da qualche altro thread, quindi onPreExecute potrebbe sparare, non sono sicuro al 100% qui, ma il thread in background non verrà creato ed eseguito.

Se si sta creando l'istanza del vostro AsyncTask da un thread in background, o qualche altra operazione non avrà luogo il thread dell'interfaccia utente principale, si potrebbe considerare l'utilizzo del metodo: Activity.runOnUiThread(Runnable)

si avrebbe bisogno l'accesso ad un istanza della tua attività in esecuzione per chiamare quel metodo, ma ti permetterà di eseguire il codice sul thread dell'interfaccia utente da un altro codice che non è in esecuzione sul thread dell'interfaccia utente.

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

David

+0

aggiungendo alla tua risposta questo thread ha una risposta interessante http://stackoverflow.com/questions/4080808/asynctask-doinbackground-does-not-run. – manjusg

+0

Grazie per l'ottima risposta! Ho sollevato questo perché penso che potrebbe essere un problema comune per principianti AsyncTask. Ahimè, non è la risposta giusta per questo problema. Entrambe le classi sono istanziate in onCreate() di un'attività in esecuzione sul thread principale dell'interfaccia utente. Ho solo un'attività in questo progetto. – SeruK

+0

@manjusg Ho sempre considerato che avesse qualcosa a che fare con il fatto che AsyncTask fosse instabile, forse molto di più quando molti sono eseguiti simultaneamente. Se è così, perché? – SeruK

0

penso che sia il sdk. Ho avuto lo stesso problema, e dopo aver cambiato l'SDK di destinazione da 15 a 11, tutto funziona perfettamente.

con sdk15, anche se AsyncTask.Status è IN CORSO, doInBackground non viene mai chiamato. penso che abbia qualcosa a che fare con il thread ui.

+0

Non posso né negare né confermare poiché non ho proprio il momento giusto per testarlo. Tutto quello che posso dire è che stavo usando l'SDK 15, quindi è molto probabile. – SeruK

6

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

Per rispondere a perché questo accade controllare questo answer (comportamento diverso a seconda del SDK)

Tuttavia, per il vostro caso, questo ostacolo può essere evitato utilizzando executeOnExecutor (disponibile a partire da 3,0 lavorato per me usando 4.0.3) ma attenzione alle limitazioni delle dimensioni e dell'accodamento del pool di thread.

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 nel docs In sostanza solo per evitare tutti i problemi che possono provenire da multithreading come intereference ....

157

Dovresti dare un'occhiata a questa risposta: https://stackoverflow.com/a/10406894/347565 e il link ai gruppi di google che include.

ho avuto un problema simile, come voi, ancora chiaro il motivo per cui non funziona, ma ho cambiato il mio codice come questo ed il problema è andato:

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

Grazie .. Ha funzionato per me .. – Rookie

+1

Ha funzionato anche per me. GRAZIE !!! – user501223

+0

Sono stato molto brutto a guardare indietro a questo post; Mi sono allontanato da tempo dal progetto. La accetterò semplicemente come risposta poiché la gente sembra dire che funzioni. – SeruK

1

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

2

Android è brutale! Non ci posso credere, quale implementazione flakey cambia da un giorno all'altro. Un giorno è un thread singolo, il successivo è il 5 e l'altro è 128.

In ogni caso, ecco una sostituzione quasi in esaurimento per il magazzino AsyncTask. Puoi anche chiamarlo AsyncTask se lo desideri, ma per evitare confusione è chiamato ThreadedAsyncTask. Devi chiamare executeStart() invece di eseguire 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); 
    } 
} 
+0

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

+0

Sì, su Android 3.0+ se non si utilizza AsyncTask.THREAD_POEC_EXECUTOR si ottiene solo un pool di thread di uno. Provalo per te, due AsyncTask e dormi nel doInBackground. Dalla documentazione AsyncTask di Android: "A partire da HONEYCOMB, le attività vengono eseguite su un singolo thread per evitare errori di applicazione comuni causati dall'esecuzione parallela." –

0

in base alla risposta di Matthieu, sotto un aiutante classe per eseguire il AsyncTask correttamente, a seconda della versione SDK in modo da evitare di duplicare il codice nell'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); 
5

È possibile farlo in due modi:

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

In caso di modo 1 non funziona per voi provare modo 2.

Way 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 vi aiuterà.

+0

super amico funziona ma i successivi compiti Asincroni che sono utilizzando AsyncTask.THREAD_POOL_EXECUTOR si stanno fallendo, quindi ho bisogno di cambiare con CustomExecutor su tutto il progetto credo :( – kumar

+0

@vinu, suggerisco di utilizzare l'attività asincrona comune e il metodo comune per eseguire AsyncTask. Spero che questo ti possa aiutare. –

+1

Ehi, questo mi ha aiutato molto! Grazie! –

Problemi correlati