Arbeiten mit dem UI-Thread in Xamarin.iOS

Anwendungsbenutzeroberflächen sind immer singlethreads, auch auf Multithread-Geräten– es gibt nur eine Darstellung des Bildschirms, und alle Änderungen an dem angezeigten Element müssen über einen einzigen "Zugriffspunkt" koordiniert werden. Dadurch wird verhindert, dass mehrere Threads gleichzeitig versuchen, dasselbe Pixel zu aktualisieren (z. B.).

Ihr Code sollte nur Änderungen an Benutzeroberflächensteuerelementen aus dem Thread Standard (oder UI) vornehmen. Alle UI-Updates, die in einem anderen Thread (z. B. einem Rückruf oder Hintergrundthread) auftreten, werden möglicherweise nicht auf dem Bildschirm gerendert oder können sogar zu einem Absturz führen.

UI-Threadausführung

Wenn Sie Steuerelemente in einer Ansicht erstellen oder ein vom Benutzer initiiertes Ereignis wie eine Toucheingabe behandeln, wird der Code bereits im Kontext des UI-Threads ausgeführt.

Wenn Code in einem Hintergrundthread ausgeführt wird, wird er in einer Aufgabe oder einem Rückruf wahrscheinlich nicht im Standard UI-Thread ausgeführt. In diesem Fall sollten Sie den Code in einen Aufruf InvokeOnMainThreadBeginInvokeOnMainThread oder wie folgt umschließen:

InvokeOnMainThread ( () => {
    // manipulate UI controls
});

Die InvokeOnMainThread Methode ist so NSObject definiert, dass sie innerhalb von Methoden aufgerufen werden kann, die für jedes UIKit-Objekt definiert sind (z. B. ein View- oder View Controller).

Beim Debuggen von Xamarin.iOS-Anwendungen wird ein Fehler ausgelöst, wenn Ihr Code versucht, über den falschen Thread auf ein UI-Steuerelement zuzugreifen. Dies hilft Ihnen, diese Probleme mit der InvokeOnMainThread-Methode nachzuverfolgen und zu beheben. Dies tritt nur beim Debuggen auf und löst keinen Fehler in Releasebuilds aus. Die Fehlermeldung wird wie folgt angezeigt:

UI-Threadausführung

Beispiel für Hintergrundthread

Hier ist ein Beispiel, das versucht, über einen einfachen Thread auf ein Benutzeroberflächensteuerelement (a UILabel) aus einem Hintergrundthread zuzugreifen:

new System.Threading.Thread(new System.Threading.ThreadStart(() => {
    label1.Text = "updated in thread"; // should NOT reference UILabel on background thread!
})).Start();

Dieser Code löst den UIKitThreadAccessException Fehler beim Debuggen aus. Um das Problem zu beheben (und sicherzustellen, dass nur über den Standard UI-Thread auf das Benutzeroberflächensteuerelement zugegriffen wird), schließen Sie code ein, der auf UI-Steuerelemente in einem InvokeOnMainThread Ausdruck verweist:

new System.Threading.Thread(new System.Threading.ThreadStart(() => {
    InvokeOnMainThread (() => {
        label1.Text = "updated in thread"; // this works!
    });
})).Start();

Sie müssen dies nicht für die neu Standard der der Beispiele in diesem Dokument verwenden, aber es ist ein wichtiges Konzept, sich zu merken, wenn Ihre App Netzwerkanforderungen sendet, das Benachrichtigungscenter oder andere Methoden verwendet, die einen Abschlusshandler erfordern, der auf einem anderen Thread ausgeführt wird.

Beispiel für Async/Await

Wenn Sie die C# 5 async/await-Schlüsselwort (keyword) verwendenInvokeOnMainThread, ist nicht erforderlich, da die Methode nach Abschluss einer erwarteten Aufgabe im aufrufenden Thread fortgesetzt wird.

Dieser Beispielcode (der auf einen Delay-Methodenaufruf wartet, rein zu Demonstrationszwecken) zeigt eine asynchrone Methode an, die im UI-Thread aufgerufen wird (es handelt sich um einen TouchUpInside-Handler). Da die enthaltende Methode im UI-Thread aufgerufen wird, können UI-Vorgänge wie das Festlegen des Texts auf einem UILabel oder einer UIAlertView Anzeige sicher aufgerufen werden, nachdem asynchrone Vorgänge in Hintergrundthreads abgeschlossen wurden.

async partial void button2_TouchUpInside (UIButton sender)
{
    textfield1.ResignFirstResponder ();
    textfield2.ResignFirstResponder ();
    textview1.ResignFirstResponder ();
    label1.Text = "async method started";
    await Task.Delay(1000); // example purpose only
    label1.Text = "1 second passed";
    await Task.Delay(2000);
    label1.Text = "2 more seconds passed";
    await Task.Delay(1000);
    new UIAlertView("Async method complete", "This method", 
               null, "Cancel", null)
        .Show();
    label1.Text = "async method completed";
}

Wenn eine asynchrone Methode aus einem Hintergrundthread (nicht aus dem Standard UI-Thread) aufgerufen wird, InvokeOnMainThread wäre weiterhin erforderlich.