Ich bin etwas verwirrt darüber, wie und wann ich es verwenden soll beginBackgroundTaskWithExpirationHandler
.
Apple zeigt in seinen Beispielen, wie man es als applicationDidEnterBackground
Delegat verwendet, um mehr Zeit für die Ausführung einer wichtigen Aufgabe zu erhalten, normalerweise einer Netzwerktransaktion.
Wenn ich auf meine App schaue, scheint es, dass die meisten meiner Netzwerk-Inhalte wichtig sind, und wenn eine gestartet wird, möchte ich sie abschließen, wenn der Benutzer die Home-Taste drückt.
Ist es also akzeptabel / eine gute Praxis, jede Netzwerktransaktion (und ich spreche nicht über das Herunterladen großer Datenmengen, meistens einige kurze XML-Dateien) zu verpacken beginBackgroundTaskWithExpirationHandler
, um auf der sicheren Seite zu sein?
Antworten:
Wenn Sie möchten, dass Ihre Netzwerktransaktion im Hintergrund fortgesetzt wird, müssen Sie sie in eine Hintergrundaufgabe einschließen. Es ist auch sehr wichtig, dass Sie anrufen,
endBackgroundTask
wenn Sie fertig sind. Andernfalls wird die App nach Ablauf der zugewiesenen Zeit beendet.Meine neigen dazu, ungefähr so auszusehen:
Ich habe eine
UIBackgroundTaskIdentifier
Eigenschaft für jede HintergrundaufgabeÄquivalenter Code in Swift
quelle
Die akzeptierte Antwort ist sehr hilfreich und sollte in den meisten Fällen in Ordnung sein, aber zwei Dinge haben mich daran gestört:
Wie eine Reihe von Personen festgestellt hat, bedeutet das Speichern der Aufgabenkennung als Eigenschaft, dass sie überschrieben werden kann, wenn die Methode mehrmals aufgerufen wird. Dies führt zu einer Aufgabe, die erst dann ordnungsgemäß beendet wird, wenn sie vom Betriebssystem zum Zeitpunkt des Ablaufs beendet werden muss .
Dieses Muster erfordert für jeden Aufruf eine eindeutige Eigenschaft,
beginBackgroundTaskWithExpirationHandler
die umständlich erscheint, wenn Sie eine größere App mit vielen Netzwerkmethoden haben.Um diese Probleme zu lösen, habe ich einen Singleton geschrieben, der sich um alle Installationen kümmert und aktive Aufgaben in einem Wörterbuch verfolgt. Es sind keine Eigenschaften erforderlich, um die Aufgabenkennungen zu verfolgen. Scheint gut zu funktionieren. Die Verwendung wird vereinfacht, um:
Wenn Sie einen Abschlussblock bereitstellen möchten, der über das Beenden der integrierten Aufgabe hinausgeht, können Sie optional Folgendes aufrufen:
Relevanter Quellcode unten verfügbar (Singleton-Material aus Gründen der Kürze ausgeschlossen). Kommentare / Feedback willkommen.
quelle
typedef
CompletionBlock? Einfach das:typedef void (^CompletionBlock)();
Hier ist eine Swift-Klasse , die das Ausführen einer Hintergrundaufgabe kapselt:
Der einfachste Weg, es zu benutzen:
Wenn Sie vor dem Beenden auf einen Rückruf eines Delegierten warten müssen, verwenden Sie Folgendes:
quelle
begin
Methode bietet, aber es ist leicht zu erkennen, wie diese Funktion hinzugefügt wird.Wie hier und in den Antworten auf andere SO-Fragen erwähnt, möchten Sie NICHT
beginBackgroundTask
nur verwenden, wenn Ihre App in den Hintergrund tritt. im Gegenteil, sollten Sie eine Hintergrundaufgabe für verwenden jederzeitaufwendiger Vorgang , dessen Abschluss Sie wollen auch , um sicherzustellen , wenn die App nicht in den Hintergrund rücken .Daher wird Ihr Code wahrscheinlich mit Wiederholungen desselben Boilerplate-Codes zum Aufrufen
beginBackgroundTask
undendBackgroundTask
kohärent gespickt sein . Um diese Wiederholung zu verhindern, ist es sicherlich vernünftig, die Boilerplate in eine einzelne gekapselte Einheit zu packen.Ich mag einige der vorhandenen Antworten dafür, aber ich denke, der beste Weg ist, eine Operations-Unterklasse zu verwenden:
Sie können die Operation in eine beliebige OperationQueue einreihen und diese Warteschlange nach Belieben bearbeiten. Sie können beispielsweise vorhandene Vorgänge in der Warteschlange vorzeitig abbrechen.
Wenn Sie mehr als eine Aufgabe haben, können Sie mehrere Vorgänge für Hintergrundaufgaben verketten. Operationen unterstützen Abhängigkeiten.
Die Operationswarteschlange kann (und sollte) eine Hintergrundwarteschlange sein. somit gibt es keine Notwendigkeit , über die Durchführung asynchrone Codes innerhalb Ihrer Aufgabe zu kümmern, weil der Betrieb ist der asynchrone Code. (In der Tat ist es nicht sinnvoll, eine andere Ebene von asynchronem Code innerhalb einer Operation auszuführen , da die Operation beendet würde, bevor dieser Code überhaupt gestartet werden könnte. Wenn Sie dies tun müssten, würden Sie eine andere Operation verwenden.)
Hier ist eine mögliche Operations-Unterklasse:
Es sollte offensichtlich sein, wie dies verwendet wird, aber falls dies nicht der Fall ist, stellen Sie sich vor, wir haben eine globale OperationQueue:
Für einen typischen zeitaufwändigen Code-Stapel würden wir also sagen:
Wenn Ihr zeitaufwändiger Codestapel in Phasen unterteilt werden kann, möchten Sie sich möglicherweise frühzeitig zurückziehen, wenn Ihre Aufgabe abgebrochen wird. In diesem Fall kehren Sie einfach vorzeitig vom Verschluss zurück. Beachten Sie, dass Ihr Verweis auf die Aufgabe innerhalb des Abschlusses schwach sein muss, sonst erhalten Sie einen Aufbewahrungszyklus. Hier ist eine künstliche Illustration:
Falls Sie eine Bereinigung durchführen müssen, falls die Hintergrundaufgabe selbst vorzeitig abgebrochen wird, habe ich eine optionale
cleanup
Handler-Eigenschaft bereitgestellt (in den vorhergehenden Beispielen nicht verwendet). Einige andere Antworten wurden dafür kritisiert, dass sie dies nicht beinhalteten.quelle
Ich habe Joels Lösung implementiert. Hier ist der vollständige Code:
.h Datei:
.m Datei:
quelle