AsyncTask è veramente incompleta o manca qualcosa?

Ho esaminato questo problema per mesi, sono venuto a trovare soluzioni diverse, che non sono contento perché sono tutti hacks massicci. Non riesco ancora a credere che una class che difetti nel design lo abbia fatto nel quadro e nessuno sta parlando, quindi immagino che devo solo mancare qualcosa.

Il problema è con AsyncTask . Secondo la documentazione che

  • Android 1.6 TabHost all'interno di un frammento
  • Non avviare automaticamente l'applicazione quando eseguo lo studio android
  • java.lang.IllegalStateException: FirebaseApp con nome
  • Android Studio Imansible trovare il metodo runProguard () per gli argomenti?
  • BottomNavigationView non è pieno
  • Il text del button Android non è centrato quando si utilizza API 17 per rendere
  • "consente di eseguire operazioni di background e pubblicare i risultati sul thread UI senza wherer manipolare thread e / o gestori".

    L'esempio continua quindi a mostrare come un metodo showDialog() esemplare viene chiamato onPostExecute() . Questo però sembra interamente contrario a me, perché la visualizzazione di una window di dialogo ha sempre bisogno di un riferimento a un Context valido e un'AsyncTask non deve mai avere un forte riferimento ad un object contestuale .

    La ragione è evidente: cosa succede se l'attività viene distrutta che ha innescato l'attività? Ciò può accadere tutto il tempo, ad esempio perché hai girato la schermata. Se l'attività conteneva un riferimento al context che l'ha creata, non si è solo tenuto in un inutile object di context (la window sarà stata distrutta e qualsiasi interazione UI fallirà con un'exception!), Anche la possibilità di creare un perdita di memory.

    A less che la mia logica non sia fatta qui, questo si traduce in: onPostExecute() è del tutto inutile, perché qual è il bene per questo metodo di eseguire sul thread UI se non si ha l'accesso a qualsiasi context? Non puoi fare niente di significativo qui.

    Una soluzione alternativa sarebbe quella di non passare istanze di context in un'ASyncTask, ma un'istanza Handler . Ciò funziona: poiché un Gestore lega leggermente il context e l'attività, è ansible scambiare messaggi tra di loro senza rischiare una perdita (destra?). Ma questo significa che la premessa di AsyncTask, cioè che non devi preoccuparti con i gestori, è sbagliata. Sembra anche abusare di Handler, dal momento che stai inviando e ricevendo messaggi sullo stesso thread (lo crei sul thread UI e lo invia in onPostExecute () che viene eseguito anche sul thread UI).

    In cima tutto fuori, anche con quella soluzione, è ancora il problema che quando il context viene distrutto, non si dispone di record dei compiti che ha sparato. Ciò significa che è necessario riavviare qualsiasi attività durante la creazione del context, ad esempio dopo una modifica dell'origine dello schermo. Questo è lento e sprecato.

    La mia soluzione a questo (come implementata nella libreria Droid-Fu ) è mantenere una mapping di WeakReference s dai nomi dei componenti alle loro istanze attuali sull'object di applicazione univoco. Ogni volta che viene avviata un'AsyncTask, registra il context di chiamata in quella mappa e su each richiamata riceverà l'istanza di context corrente da tale mapping. Ciò assicura che non si riferirà mai a un'istanza di context scadente e si ha sempre accesso a un context valido nelle chiamate in modo da poter svolgere un ottimo lavoro UI. Inoltre, non viene perduto, perché i riferimenti sono deboli e vengono eliminati quando non esiste più alcun esempio di un determinato componente.

    Eppure, è una soluzione complessiva e richiede di sottodistrare alcune delle classi della libreria Droid-Fu, rendendo questo un approccio piuttosto intrusivo.

    Ora voglio semplicemente sapere: mi manca solo qualcosa di enorme o è AsyncTask davvero totalmente difettoso? Come funzionano le vostre esperienze con esso? Come hai risolto questi problemi?

    Grazie per il tuo contributo.

  • Come disabilitare i messaggi di toast generati in Pinning dello schermo?
  • Come fare un'image in una cornice circolare in android
  • utilizzando sendBroadcast in un'applicazione di sistema
  • Come sviluppare un'applicazione che dispone di componenti aggiuntivi?
  • Calendario di Android Calendar con il giorno della settimana
  • Testare il regno sotto Android
  • 12 Solutions collect form web for “AsyncTask è veramente incompleta o manca qualcosa?”

    Che ne dici di qualcosa del genere:

     class MyActivity extends Activity { Worker mWorker; static class Worker extends AsyncTask<URL, Integer, Long> { MyActivity mActivity; Worker(MyActivity activity) { mActivity = activity; } @Override protected Long doInBackground(URL... urls) { int count = urls.length; long totalSize = 0; for (int i = 0; i < count; i++) { totalSize += Downloader.downloadFile(urls[i]); publishProgress((int) ((i / (float) count) * 100)); } return totalSize; } @Override protected void onProgressUpdate(Integer... progress) { if (mActivity != null) { mActivity.setProgressPercent(progress[0]); } } @Override protected void onPostExecute(Long result) { if (mActivity != null) { mActivity.showDialog("Downloaded " + result + " bytes"); } } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mWorker = (Worker)getLastNonConfigurationInstance(); if (mWorker != null) { mWorker.mActivity = this; } ... } @Override public Object onRetainNonConfigurationInstance() { return mWorker; } @Override protected void onDestroy() { super.onDestroy(); if (mWorker != null) { mWorker.mActivity = null; } } void startWork() { mWorker = new Worker(this); mWorker.execute(...); } } 

    La ragione è evidente: cosa succede se l'attività viene distrutta che ha innescato l'attività?

    AsyncTask manualmente l'attività AsyncTask in onDestroy() . AsyncTask manualmente la nuova attività AsyncTask in onCreate() . Ciò richiede una class interna statica o una class Java standard, più forse 10 righe di codice.

    Sembra che AsyncTask è un po ' più che solo concettualmente difettoso . È anche inutilizzabile da problemi di compatibilità. I documenti Android hanno letto:

    Quando è stato introdotto per la prima volta, AsyncTasks è stato eseguito serialmente su un singolo thread di background. A partire da DONUT, questo è stato modificato in un gruppo di thread che consente a più attività di operare in parallelo. A partire da HONEYCOMB, le attività vengono nuovamente eseguite su un singolo thread per evitare errori di applicazione comuni causati dall'esecuzione parallela. Se si desidera veramente l'esecuzione parallela, è ansible utilizzare la versione executeOnExecutor(Executor, Params...) di questo metodo con THREAD_POOL_EXECUTOR ; però, vedi un commento in merito agli avvisi relativi al suo utilizzo.

    Entrambi executeOnExecutor() e THREAD_POOL_EXECUTOR vengono aggiunti nel livello API 11 (Android 3.0.x, HONEYCOMB).

    Ciò significa che se si creano due AsyncTask s per scaricare due file, il secondo download non verrà avviato finché il primo non finisce. Se si chiede due server e il primo server è in discesa, non si connette alla seconda prima della connessione alla prima volta. (A less che non si utilizzino le nuove API11, naturalmente, ma questo renderà il codice incompatibile con 2.x).

    E se si desidera bersaglio sia 2.x che 3.0+, la roba diventa veramente difficile.

    Inoltre, i docenti dicono:

    Attenzione: un altro problema che si potrebbe incontrare quando si utilizza un thread di lavoratore è inatteso riavvia nella tua attività a causa di una modifica della configuration di runtime (ad esempio quando l'utente modifica l'orientamento dello schermo), che potrebbe distruggere il thread di lavoratore . Per vedere come è ansible persistere la tua attività durante uno di questi riavvio e come annullare correttamente l'attività quando l'attività viene distrutta, vedere il codice sorgente dell'applicazione di esempio Shelves.

    Probabilmente tutti noi, incluso Google, stanno abusando AsyncTask dal punto di vista del MVC .

    Un'attività è un controller e il controller non deve avviare operazioni che possono sopravvivere alla visualizzazione . Vale a dire, le AsyncTasks dovrebbero essere usate dal model , da una class che non è legata al ciclo di vita dell'attività. Ricorda che le attività sono distrutte in rotazione. (Per quanto riguarda la vista , di solito non si programma le classi derivate da, ad esempio, android.widget.Button, ma è ansible. Di solito, l'unica cosa che fai della visualizzazione è il xml.)

    In altre parole, è sbagliato mettere i derivati ​​di AsyncTask nei methods di Attività. OTOH, se non dobbiamo utilizzare AsyncTasks in attività, AsyncTask perde la sua attrtriggers: è stata pubblicizzata come una soluzione veloce e facile.

    Non sono sicuro che sia vero che rischiate una perdita di memory con un riferimento a un context da un AsyncTask.

    Il modo consueto di attuarlo è quello di creare una nuova istanza AsyncTask nell'ambito di uno dei methods dell'attività. Quindi, se l'attività viene distrutta, allora una volta completato l'AsyncTask, non sarà irraggiungibile e quindi idoneo alla raccolta dei rifiuti? Quindi il riferimento all'attività non import perché l'AsyncTask stessa non si bloccherà.

    Sarebbe più robusto tenere un WeekReference sulla tua attività:

     public class WeakReferenceAsyncTaskTestActivity extends Activity { private static final int MAX_COUNT = 100; private ProgressBar progressBar; private AsyncTaskCounter mWorker; @SuppressWarnings("deprecation") @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_async_task_test); mWorker = (AsyncTaskCounter) getLastNonConfigurationInstance(); if (mWorker != null) { mWorker.mActivity = new WeakReference<WeakReferenceAsyncTaskTestActivity>(this); } progressBar = (ProgressBar) findViewById(R.id.progressBar1); progressBar.setMax(MAX_COUNT); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.activity_async_task_test, menu); return true; } public void onStartButtonClick(View v) { startWork(); } @Override public Object onRetainNonConfigurationInstance() { return mWorker; } @Override protected void onDestroy() { super.onDestroy(); if (mWorker != null) { mWorker.mActivity = null; } } void startWork() { mWorker = new AsyncTaskCounter(this); mWorker.execute(); } static class AsyncTaskCounter extends AsyncTask<Void, Integer, Void> { WeakReference<WeakReferenceAsyncTaskTestActivity> mActivity; AsyncTaskCounter(WeakReferenceAsyncTaskTestActivity activity) { mActivity = new WeakReference<WeakReferenceAsyncTaskTestActivity>(activity); } private static final int SLEEP_TIME = 200; @Override protected Void doInBackground(Void... params) { for (int i = 0; i < MAX_COUNT; i++) { try { Thread.sleep(SLEEP_TIME); } catch (InterruptedException e) { e.printStackTrace(); } Log.d(getClass().getSimpleName(), "Progress value is " + i); Log.d(getClass().getSimpleName(), "getActivity is " + mActivity); Log.d(getClass().getSimpleName(), "this is " + this); publishProgress(i); } return null; } @Override protected void onProgressUpdate(Integer... values) { super.onProgressUpdate(values); if (mActivity != null) { mActivity.get().progressBar.setProgress(values[0]); } } } } 

    Perché non solo ignorare il metodo onPause() nell'attività di properties; e annullare l' AsyncTask da lì?

    Hai assolutamente ragione – per questo motivo, un movimento lontano dall'utilizzo di task / caricatori asincroni nelle attività per recuperare i dati sta guadagnando slancio. Uno dei modi nuovi è quello di utilizzare un framework Volley che preveda essenzialmente una richiamata una volta che i dati sono pronti – molto più coerenti con il model MVC. Volley è stato populizzato nel I / O di Google 2013. Non so perché più persone non ne sono a conoscenza.

    Ho pensato di annullare le opere, ma non lo fa.

    qui RTFMing su di esso:

    "" Se l'attività è già stata avviata, il parametro mayInterruptIfRunning determina se il thread che esegue questa attività dovrebbe essere interrotto nel tentativo di arrestare l'attività ".

    Ciò non implica però che il filo sia interrompibile. Questa è una cosa Java, non una cosa AsyncTask. "

    http://groups.google.com/group/android-developers/browse_thread/thread/dcadb1bc7705f1bb/add136eb4949359d?show_docid=add136eb4949359d

    Sarebbe meglio pensare a AsyncTask come qualcosa che è più strettamente accoppiato ad un'attività, un context, un context di lavoro, ecc. È più comodo quando il suo field di applicazione è pienamente compreso.

    Assicurati di disporre di una politica di cancellazione nel tuo ciclo di vita in modo che alla fine sia raccolta di rifiuti e non tiene più un riferimento alla tua attività e può anche essere raccolta dei rifiuti.

    Senza annullare l'AsyncTask mentre si allontana dal context, si esegue in perdite di memory e NullPointerExceptions, se è sufficiente fornire un feedback come un Toast una semplice window di dialogo, allora un singolo componente del tuo ambiente di applicazione aiuta a evitare il problema di NPE.

    AsyncTask non è tutto male, ma c'è sicuramente un sacco di magia in corso che può portre a alcune insidie ​​impreviste.

    Personalmente, ho solo estendere Thread e utilizzare un'interface di callback per aggiornare l'UI. Non avrei mai potuto get AsyncTask per lavorare bene senza problemi FC. Utilizzo anche una coda non bloccante per gestire il pool di esecuzione.

    Per quanto riguarda le "esperienze che lavorano con esso": è ansible uccidere il process insieme a tutte le AsyncTasks, Android creerà nuovamente lo stack di attività in modo che l'utente non possa menzionare nulla.

    L'Android è un fan Android di Google, tutto su telefoni Android, Android Wear, Android Dev e applicazioni Android Games e così via.