Nuta
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Interfejsy użytkownika aplikacji są zawsze jednowątkowy, nawet w urządzeniach wielowątkowych — istnieje tylko jedna reprezentacja ekranu i wszelkie zmiany wyświetlanego elementu muszą być koordynowane za pośrednictwem jednego "punktu dostępu". Zapobiega to jednoczesnej próbie zaktualizowania tego samego piksela przez wiele wątków (na przykład).
Kod powinien wprowadzać zmiany tylko w kontrolkach interfejsu użytkownika z wątku głównego (lub interfejsu użytkownika). Wszelkie aktualizacje interfejsu użytkownika, które występują w innym wątku (takim jak wywołanie zwrotne lub wątek w tle), mogą nie zostać renderowane na ekranie lub nawet spowodować awarię.
Wykonywanie wątku interfejsu użytkownika
Podczas tworzenia kontrolek w widoku lub obsługi zdarzenia zainicjowanego przez użytkownika, takiego jak dotknięcie, kod jest już wykonywany w kontekście wątku interfejsu użytkownika.
Jeśli kod jest wykonywany w wątku w tle, w zadaniu lub wywołaniu zwrotnym prawdopodobnie nie jest wykonywany w głównym wątku interfejsu użytkownika. W takim przypadku należy opakować kod w wywołaniu metody InvokeOnMainThread lub BeginInvokeOnMainThread w następujący sposób:
InvokeOnMainThread ( () => {
// manipulate UI controls
});
Metoda jest zdefiniowana w NSObject elemecie InvokeOnMainThread , więc można ją wywołać z metod zdefiniowanych na dowolnym obiekcie UIKit (takim jak Widok lub Kontroler widoku).
Podczas debugowania aplikacji platformy Xamarin.iOS zostanie zgłoszony błąd, jeśli kod próbuje uzyskać dostęp do kontrolki interfejsu użytkownika z nieprawidłowego wątku. Ułatwia to śledzenie i rozwiązywanie tych problemów za pomocą metody InvokeOnMainThread. Dzieje się tak tylko podczas debugowania i nie zgłasza błędu w kompilacjach wydania. Komunikat o błędzie będzie wyświetlany następująco:

Przykład wątku tła
Oto przykład, który próbuje uzyskać dostęp do kontrolki interfejsu użytkownika (a UILabel) z wątku w tle przy użyciu prostego wątku:
new System.Threading.Thread(new System.Threading.ThreadStart(() => {
label1.Text = "updated in thread"; // should NOT reference UILabel on background thread!
})).Start();
Ten kod zgłosi błąd UIKitThreadAccessException podczas debugowania. Aby rozwiązać ten problem (i upewnić się, że kontrola interfejsu użytkownika jest dostępna tylko z głównego wątku interfejsu użytkownika), opakuj dowolny kod odwołujący się do kontrolek interfejsu użytkownika wewnątrz InvokeOnMainThread wyrażenia w następujący sposób:
new System.Threading.Thread(new System.Threading.ThreadStart(() => {
InvokeOnMainThread (() => {
label1.Text = "updated in thread"; // this works!
});
})).Start();
Nie trzeba tego używać w pozostałej części przykładów w tym dokumencie, ale ważne jest, aby pamiętać, kiedy aplikacja wysyła żądania sieciowe, używa centrum powiadomień lub innych metod wymagających obsługi ukończenia, która będzie uruchamiana w innym wątku.
Przykład Async/Await
W przypadku używania słów kluczowych asynchronicznych InvokeOnMainThread /await w języku C# 5 nie jest wymagane, ponieważ w przypadku ukończenia oczekiwanego zadania metoda będzie kontynuowana w wątku wywołującym.
Ten przykładowy kod (który oczekuje na wywołanie metody Delay, wyłącznie w celach demonstracyjnych) pokazuje metodę asynchroniczną wywoływaną w wątku interfejsu użytkownika (jest to procedura obsługi TouchUpInside). Ponieważ metoda zawierająca jest wywoływana w wątku interfejsu użytkownika, operacje interfejsu użytkownika, takie jak ustawianie tekstu na UILabel obiekcie lub wyświetlanie UIAlertView elementu, można bezpiecznie wywołać po zakończeniu operacji asynchronicznych w wątkach w tle.
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";
}
Jeśli metoda asynchronikowa jest wywoływana z wątku w tle (a nie głównego wątku interfejsu użytkownika), InvokeOnMainThread nadal będzie wymagana.