Ist AsyncTask wirklich konzeptionell fehlerhaft oder fehlt mir nur etwas?


264

Ich habe dieses Problem seit Monaten untersucht und verschiedene Lösungen gefunden, mit denen ich nicht zufrieden bin, da es sich um massive Hacks handelt. Ich kann immer noch nicht glauben, dass eine Klasse, die im Design fehlerhaft war, es in das Framework geschafft hat und niemand darüber spricht, also muss mir wohl etwas fehlen.

Das Problem ist mit AsyncTask. Laut Dokumentation ist es

"Ermöglicht das Ausführen von Hintergrundoperationen und das Veröffentlichen von Ergebnissen auf dem UI-Thread, ohne dass Threads und / oder Handler bearbeitet werden müssen."

Das Beispiel zeigt dann weiterhin, wie eine beispielhafte showDialog()Methode aufgerufen wird onPostExecute(). Dies scheint mir jedoch völlig erfunden zu sein, da das ContextAnzeigen eines Dialogfelds immer einen Verweis auf ein gültiges Objekt erfordert und eine AsyncTask niemals einen starken Verweis auf ein Kontextobjekt enthalten darf .

Der Grund liegt auf der Hand: Was passiert, wenn die Aktivität zerstört wird, die die Aufgabe ausgelöst hat? Dies kann die ganze Zeit passieren, z. B. weil Sie den Bildschirm umgedreht haben. Wenn die Aufgabe einen Verweis auf den Kontext enthält, der sie erstellt hat, halten Sie nicht nur an einem nutzlosen Kontextobjekt fest (das Fenster wurde zerstört und jede UI-Interaktion schlägt mit einer Ausnahme fehl!), Sondern Sie riskieren sogar das Erstellen eines Speicherleck.

Sofern meine Logik hier nicht fehlerhaft ist, bedeutet dies: onPostExecute()ist völlig nutzlos, denn was nützt es, wenn diese Methode auf dem UI-Thread ausgeführt wird, wenn Sie keinen Zugriff auf einen Kontext haben? Sie können hier nichts Sinnvolles tun.

Eine Problemumgehung besteht darin, keine Kontextinstanzen an eine AsyncTask zu übergeben, sondern eine HandlerInstanz. Das funktioniert: Da ein Handler den Kontext und die Aufgabe lose bindet, können Sie Nachrichten zwischen ihnen austauschen, ohne ein Leck zu riskieren (richtig?). Dies würde jedoch bedeuten, dass die Prämisse von AsyncTask, dass Sie sich nicht um Handler kümmern müssen, falsch ist. Es scheint auch so, als würde man Handler missbrauchen, da Sie Nachrichten im selben Thread senden und empfangen (Sie erstellen sie im UI-Thread und senden sie in onPostExecute () durch, das auch im UI-Thread ausgeführt wird).

Um das Ganze abzurunden, haben Sie trotz dieser Problemumgehung immer noch das Problem, dass Sie bei Zerstörung des Kontexts keine Aufzeichnungen über die von ihm ausgelösten Aufgaben haben. Das bedeutet, dass Sie alle Aufgaben neu starten müssen, wenn Sie den Kontext neu erstellen, z. B. nach einer Änderung der Bildschirmausrichtung. Das ist langsam und verschwenderisch.

Meine Lösung hierfür (wie in der Droid-Fu-Bibliothek implementiert ) besteht darin, eine Zuordnung von WeakReferences von Komponentennamen zu ihren aktuellen Instanzen auf dem eindeutigen Anwendungsobjekt beizubehalten . Jedes Mal, wenn eine AsyncTask gestartet wird, zeichnet sie den aufrufenden Kontext in dieser Zuordnung auf und ruft bei jedem Rückruf die aktuelle Kontextinstanz aus dieser Zuordnung ab. Dies stellt sicher, dass Sie niemals auf eine veraltete Kontextinstanz verweisen und in den Rückrufen immer Zugriff auf einen gültigen Kontext haben, damit Sie dort sinnvolle UI-Arbeiten ausführen können. Es leckt auch nicht, da die Referenzen schwach sind und gelöscht werden, wenn keine Instanz einer bestimmten Komponente mehr vorhanden ist.

Trotzdem ist es eine komplexe Problemumgehung und erfordert die Unterklasse einiger Droid-Fu-Bibliotheksklassen, was dies zu einem ziemlich aufdringlichen Ansatz macht.

Jetzt möchte ich einfach nur wissen: Vermisse ich nur massiv etwas oder ist AsyncTask wirklich völlig fehlerhaft? Wie arbeiten Ihre Erfahrungen damit? Wie haben Sie dieses Problem gelöst?

Danke für deinen Beitrag.


1
Wenn Sie neugierig sind, haben wir kürzlich der Zündkernbibliothek eine Klasse namens IgnitedAsyncTask hinzugefügt, die den typsicheren Kontextzugriff in allen Rückrufen mithilfe des von Dianne unten beschriebenen Verbindungs- / Trennungsmusters unterstützt. Außerdem können Ausnahmen ausgelöst und in einem separaten Rückruf behandelt werden. Siehe github.com/kaeppler/ignition-core/blob/master/src/com/github/…
Matthias

Schauen Sie sich das an: gist.github.com/1393552
Matthias

1
Diese Frage ist auch verwandt.
Alex Lockwood

Ich füge die asynchronen Aufgaben einer Arrayliste hinzu und stelle sicher, dass sie alle an einem bestimmten Punkt geschlossen werden.
NightSkyCode

Antworten:


86

Wie wäre es mit so etwas:

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

5
Ja, mActivity ist! = Null, aber wenn es keine Verweise auf Ihre Worker-Instanz gibt, werden alle Verweise auf diese Instanz ebenfalls der Müllabfuhr unterzogen. Wenn Ihre Aufgabe für immer ausgeführt wird, liegt ohnehin ein Speicherverlust vor (Ihre Aufgabe) - ganz zu schweigen davon, dass Sie den Akku des Telefons entladen. Außerdem können Sie, wie an anderer Stelle erwähnt, mActivity in onDestroy auf null setzen.
EboMike

13
Die onDestroy () -Methode setzt mActivity auf null. Es spielt keine Rolle, wer zuvor einen Verweis auf die Aktivität hat, da diese noch ausgeführt wird. Das Fenster der Aktivität ist immer gültig, bis onDestroy () aufgerufen wird. Wenn Sie dort den Wert auf Null setzen, erkennt die asynchrone Aufgabe, dass die Aktivität nicht mehr gültig ist. (Und wenn sich eine Konfiguration ändert, wird onDestroy () der vorherigen Aktivität aufgerufen und die onCreate () der nächsten Aktivität ohne Nachrichten in der zwischen ihnen verarbeiteten Hauptschleife ausgeführt, sodass die AsyncTask niemals einen inkonsistenten Status sieht.)
hackbod

8
stimmt, aber es löst immer noch nicht das letzte Problem, das ich erwähnt habe: Stellen Sie sich vor, die Aufgabe lädt etwas aus dem Internet herunter. Wenn Sie bei diesem Ansatz den Bildschirm dreimal umdrehen, während die Aufgabe ausgeführt wird, wird er bei jeder Bildschirmdrehung neu gestartet, und jede Aufgabe außer der letzten wirft ihr Ergebnis weg, da ihre Aktivitätsreferenz null ist.
Matthias

11
Um im Hintergrund darauf zugreifen zu können, müssten Sie entweder die entsprechende Synchronisation um mActivity herum durchführen und sich mit Zeiten befassen, in denen sie null ist, oder der Hintergrund-Thread muss nur Context.getApplicationContext () verwenden, eine einzelne globale Instanz für die App. Der Anwendungskontext ist in Ihren Möglichkeiten eingeschränkt (keine Benutzeroberfläche wie z. B. Dialog) und erfordert einige Sorgfalt (registrierte Empfänger und Dienstbindungen bleiben für immer erhalten, wenn Sie sie nicht bereinigen), ist jedoch im Allgemeinen für Code geeignet, der nicht vorhanden ist ist nicht an den Kontext einer bestimmten Komponente gebunden.
Hackbod

4
Das war unglaublich hilfreich, danke Dianne! Ich wünschte, die Dokumentation wäre in erster Linie so gut.
Matthias

20

Der Grund liegt auf der Hand: Was ist, wenn die Aktivität zerstört wird, die die Aufgabe ausgelöst hat?

Trennen Sie die Aktivität manuell von der AsyncTaskin onDestroy(). Ordnen Sie die neue Aktivität dem AsyncTaskIn manuell zu onCreate(). Dies erfordert entweder eine statische innere Klasse oder eine Standard-Java-Klasse sowie möglicherweise 10 Codezeilen.


Seien Sie vorsichtig mit statischen Referenzen - Ich habe gesehen, dass Objekte durch Müll gesammelt wurden, obwohl es statische starke Referenzen zu ihnen gab. Möglicherweise ein Nebeneffekt des Android-Klassenladeprogramms oder sogar ein Fehler, aber statische Referenzen sind keine sichere Möglichkeit, den Status über einen Aktivitätslebenszyklus hinweg auszutauschen. Das App-Objekt ist jedoch, warum ich das benutze.
Matthias

10
@ Matthias: Ich habe nicht gesagt, statische Referenzen zu verwenden. Ich sagte, ich solle eine statische innere Klasse verwenden. Es gibt einen wesentlichen Unterschied, obwohl beide "statisch" in ihren Namen sind.
CommonsWare


5
Ich verstehe - der Schlüssel hier ist getLastNonConfigurationInstance (), nicht die statische innere Klasse. Eine statische innere Klasse enthält keinen impliziten Verweis auf ihre äußere Klasse, sodass sie semantisch einer einfachen öffentlichen Klasse entspricht. Nur eine Warnung: onRetainNonConfigurationInstance () wird NICHT garantiert aufgerufen, wenn eine Aktivität unterbrochen wird (eine Unterbrechung kann auch ein Telefonanruf sein), daher müssten Sie Ihre Aufgabe auch in onSaveInstanceState () parzellieren, um eine wirklich solide Funktion zu erzielen Lösung. Aber trotzdem schöne Idee.
Matthias

7
Um ... onRetainNonConfigurationInstance () wird immer aufgerufen, wenn die Aktivität gerade zerstört und neu erstellt wird. Es macht keinen Sinn, zu anderen Zeiten anzurufen. Wenn zu einer anderen Aktivität gewechselt wird, wird die aktuelle Aktivität angehalten / gestoppt, aber nicht zerstört, sodass die asynchrone Aufgabe weiterhin ausgeführt werden kann und dieselbe Aktivitätsinstanz verwendet. Wenn es beendet ist und ein Dialogfeld angezeigt wird, wird das Dialogfeld als Teil dieser Aktivität korrekt angezeigt und dem Benutzer daher erst angezeigt, wenn er zur Aktivität zurückkehrt. Sie können AsyncTask nicht in ein Bundle einfügen.
Hackbod

15

Es sieht so aus, als wäre AsyncTaskes ein bisschen mehr als nur konzeptionell fehlerhaft . Es ist auch durch Kompatibilitätsprobleme unbrauchbar. Die Android-Dokumente lauten:

Bei der ersten Einführung wurden AsyncTasks seriell auf einem einzelnen Hintergrundthread ausgeführt. Beginnend mit DONUT wurde dies in einen Thread-Pool geändert, sodass mehrere Aufgaben gleichzeitig ausgeführt werden können. Beim Starten von HONEYCOMB werden Aufgaben wieder in einem einzelnen Thread ausgeführt, um häufige Anwendungsfehler zu vermeiden, die durch parallele Ausführung verursacht werden. Wenn Sie wirklich eine parallele Ausführung wünschen, können Sie die executeOnExecutor(Executor, Params...) Version dieser Methode mit verwenden THREAD_POOL_EXECUTOR . Im Kommentar finden Sie jedoch Warnungen zu seiner Verwendung.

Beide executeOnExecutor()und THREAD_POOL_EXECUTORwerden in API Level 11 (Android 3.0.x, HONEYCOMB) hinzugefügt.

Dies bedeutet, dass wenn Sie zwei AsyncTasks erstellen , um zwei Dateien herunterzuladen, der zweite Download erst beginnt, wenn der erste abgeschlossen ist. Wenn Sie über zwei Server chatten und der erste Server ausfällt, stellen Sie keine Verbindung zum zweiten Server her, bevor die Verbindung zum ersten Server unterbrochen wird. (Es sei denn, Sie verwenden natürlich die neuen API11-Funktionen, aber dadurch wird Ihr Code mit 2.x nicht kompatibel.)

Und wenn Sie sowohl auf 2.x als auch auf 3.0+ abzielen möchten, wird das Zeug wirklich knifflig.

Darüber hinaus sagen die Dokumente :

Achtung: Ein weiteres Problem, das bei der Verwendung eines Arbeitsthreads auftreten kann, ist ein unerwarteter Neustart Ihrer Aktivität aufgrund einer Änderung der Laufzeitkonfiguration (z. B. wenn der Benutzer die Bildschirmausrichtung ändert), wodurch Ihr Arbeitsthread möglicherweise zerstört wird . Informationen dazu, wie Sie Ihre Aufgabe während eines dieser Neustarts beibehalten und die Aufgabe ordnungsgemäß abbrechen können, wenn die Aktivität zerstört wird, finden Sie im Quellcode der Shelves-Beispielanwendung.


12

Wahrscheinlich missbrauchen wir alle, einschließlich Google, AsyncTaskaus MVC- Sicht.

Eine Aktivität ist ein Controller , und der Controller sollte keine Vorgänge starten, die die Ansicht möglicherweise überleben . Das heißt, AsyncTasks sollten von Model aus einer Klasse verwendet werden, die nicht an den Aktivitätslebenszyklus gebunden ist. Denken Sie daran, dass Aktivitäten bei Rotation zerstört werden. (In Bezug auf die Ansicht programmieren Sie normalerweise keine Klassen, die beispielsweise von android.widget.Button abgeleitet sind, aber Sie können dies. Normalerweise ist das einzige, was Sie an der Ansicht tun, die XML.)

Mit anderen Worten, es ist falsch, AsyncTask-Derivate in die Methoden der Aktivitäten einzufügen. OTOH, wenn wir AsyncTasks nicht in Aktivitäten verwenden dürfen, verliert AsyncTask seine Attraktivität: Früher wurde es als schnelle und einfache Lösung beworben.


5

Ich bin nicht sicher, ob es wahr ist, dass Sie einen Speicherverlust mit einem Verweis auf einen Kontext aus einer AsyncTask riskieren.

Die übliche Art, sie zu implementieren, besteht darin, eine neue AsyncTask-Instanz im Rahmen einer der Methoden der Aktivität zu erstellen. Wenn die Aktivität zerstört wird, ist sie dann nach Abschluss der AsyncTask nicht unerreichbar und kann dann zur Speicherbereinigung verwendet werden? Der Verweis auf die Aktivität spielt also keine Rolle, da die AsyncTask selbst nicht herumhängt.


2
wahr - aber was ist, wenn die Aufgabe auf unbestimmte Zeit blockiert? Aufgaben sollen Blockierungsvorgänge ausführen, möglicherweise sogar solche, die niemals beendet werden. Dort haben Sie Ihr Speicherleck.
Matthias

1
Jeder Mitarbeiter, der etwas in einer Endlosschleife ausführt oder etwas, das nur blockiert, z. B. bei einer E / A-Operation.
Matthias

2

Es wäre robuster, eine Wochenreferenz über Ihre Aktivität zu führen:

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

}

Dies ähnelt dem, was wir zuerst mit Droid-Fu gemacht haben. Wir würden eine Karte mit schwachen Verweisen auf Kontextobjekte führen und in den Task-Rückrufen nachschlagen, um die neueste Referenz (falls verfügbar) zu erhalten, für die der Rückruf ausgeführt werden soll. Unser Ansatz bedeutete jedoch, dass es eine einzige Entität gab, die diese Zuordnung beibehalten hat, während Ihr Ansatz dies nicht tut. Dies ist in der Tat besser.
Matthias

1
Haben Sie sich RoboSpice angesehen? github.com/octo-online/robospice . Ich glaube, dieses System ist noch besser.
Snicolas

Der Beispielcode auf der Startseite sieht so aus, als würde eine Kontextreferenz verloren gehen (eine innere Klasse behält eine implizite Referenz auf die äußere Klasse bei.) Nicht überzeugt !!
Matthias

@Matthias, Sie haben Recht, deshalb schlage ich eine statische innere Klasse vor, die eine schwache Referenz für die Aktivität enthält.
Snicolas

1
@Matthias, ich glaube, das fängt an, nicht zum Thema zu gehören. Loader bieten jedoch kein sofortiges Caching, da Loader in der Regel ausführlicher sind als unsere Bibliothek. Eigentlich handhaben sie Cursor ziemlich gut, aber für die Vernetzung ist ein anderer Ansatz, der auf Caching und einem Dienst basiert, besser geeignet. Siehe neilgoodman.net/2011/12/26/… Teil 1 & 2
Snicolas

1

Warum nicht einfach die onPause()Methode in der Besitzaktivität überschreiben und die AsyncTaskvon dort abbrechen ?


es hängt davon ab, was diese Aufgabe tut. Wenn nur einige Daten geladen / gelesen werden, ist dies in Ordnung. Wenn sich jedoch der Status einiger Daten auf einem Remote-Server ändert, möchten wir der Aufgabe lieber die Möglichkeit geben, sie bis zum Ende auszuführen.
Vit Khudenko

@Arhimed und ich nehme an, wenn Sie den UI-Thread so hochhalten, onPausedass er genauso schlecht ist wie irgendwo anders? Dh du könntest eine ANR bekommen?
Jeff Axelrod

genau. Wir können den UI-Thread nicht blockieren (sei es ein onPauseoder ein anderer), da wir das Risiko haben, eine ANR zu erhalten.
Vit Khudenko

1

Sie haben absolut Recht - deshalb gewinnt eine Abkehr von der Verwendung asynchroner Aufgaben / Lader in den Aktivitäten zum Abrufen von Daten an Dynamik. Eine der neuen Möglichkeiten ist die Verwendung eines Volley- Frameworks, das im Wesentlichen einen Rückruf bereitstellt, sobald die Daten bereit sind - viel konsistenter mit dem MVC-Modell. Volley wurde in Google I / O 2013 bevölkert. Ich bin mir nicht sicher, warum mehr Menschen dies nicht wissen.


danke dafür ... ich werde es untersuchen ... mein Grund dafür, dass ich AsyncTask nicht mag, ist, dass ich mich an eine Anweisung auf PostExecute halte ... es sei denn, ich hacke es wie die Verwendung von Schnittstellen oder überschreibe es jedes Mal Ich brauche es.
Carinlynchin

0

Persönlich erweitere ich nur Thread und verwende eine Rückrufschnittstelle, um die Benutzeroberfläche zu aktualisieren. Ich könnte AsyncTask ohne FC-Probleme nie richtig zum Laufen bringen. Ich verwende auch eine nicht blockierende Warteschlange, um den Ausführungspool zu verwalten.


1
Nun, Ihre Force Close war wahrscheinlich auf das von mir erwähnte Problem zurückzuführen: Sie haben versucht, auf einen Kontext zu verweisen, der außerhalb des Gültigkeitsbereichs lag (dh dessen Fenster zerstört wurde), was zu einer Framework-Ausnahme führen wird.
Matthias

Nein ... eigentlich lag es daran, dass die Warteschlange in AsyncTask integriert ist. Ich benutze immer getApplicationContext (). Ich habe keine Probleme mit AsyncTask, wenn es nur ein paar Operationen sind ... aber ich schreibe einen Media Player, der das Albumcover im Hintergrund aktualisiert ... in meinem Test habe ich 120 Alben ohne Grafik ... also, während Meine App wurde nicht vollständig geschlossen, Asynctask hat Fehler ausgelöst. Stattdessen habe ich eine Singleton-Klasse mit einer Warteschlange erstellt, die die Prozesse verwaltet. Bisher funktioniert sie hervorragend.
androidworkz

0

Ich dachte, Abbrechen funktioniert, aber es funktioniert nicht.

hier RTFMing darüber:

"" Wenn die Aufgabe bereits gestartet wurde, bestimmt der Parameter mayInterruptIfRunning, ob der Thread, der diese Aufgabe ausführt, unterbrochen werden soll, um die Aufgabe zu stoppen. "

Dies bedeutet jedoch nicht, dass der Thread unterbrechbar ist. Das ist eine Java-Sache, keine AsyncTask-Sache. "

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


0

Sie sollten sich AsyncTask besser als etwas vorstellen, das enger mit einer Aktivität, einem Kontext, einem ContextWrapper usw. verbunden ist. Es ist praktischer, wenn der Umfang vollständig verstanden wird.

Stellen Sie sicher, dass Sie in Ihrem Lebenszyklus eine Stornierungsrichtlinie haben, damit diese schließlich durch Müll gesammelt wird und keinen Verweis mehr auf Ihre Aktivität enthält und auch Müll gesammelt werden kann.

Wenn Sie Ihre AsyncTask nicht abbrechen, während Sie sich von Ihrem Kontext entfernen, treten Speicherlecks und NullPointerExceptions auf. Wenn Sie lediglich Feedback wie einen Toast-Dialog in einem einfachen Dialogfeld benötigen, kann ein Singleton Ihres Anwendungskontexts das NPE-Problem vermeiden.

AsyncTask ist nicht alles schlecht, aber es gibt definitiv viel Magie, die zu unvorhergesehenen Fallstricken führen kann.


-1

In Bezug auf „Erfahrungen mit ihm arbeiten“: es ist möglich , um den Prozess zu töten zusammen mit allen AsyncTasks, Android wird die Aktivität Stapel neu zu erstellen , so dass der Benutzer nichts erwähnen.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.