Práce s vláknem uživatelského rozhraní v Xamarin. iOS

Uživatelská rozhraní aplikace jsou vždycky v jednom vlákně, i v zařízeních s více vlákny – k dispozici je jenom jedna reprezentace obrazovky a všechny změny, které se zobrazí, musí být koordinovány prostřednictvím jediného přístupového bodu. Tím zabráníte více vláknům v pokusu o aktualizaci stejného pixelu ve stejnou dobu (například).

Váš kód by měl provádět změny pouze v ovládacích prvcích uživatelského rozhraní z hlavního vlákna (nebo uživatelského rozhraní). Jakékoli aktualizace uživatelského rozhraní, ke kterým dochází v jiném vlákně (například zpětné volání nebo vlákno na pozadí), se nemusí zobrazit na obrazovku nebo by mohly způsobit selhání.

Spuštění vlákna uživatelského rozhraní

Při vytváření ovládacích prvků v zobrazení nebo při zpracování události iniciované uživatelem, jako je třeba dotykové ovládání, je kód již spuštěn v kontextu vlákna uživatelského rozhraní.

Pokud je kód spuštěn na vlákně na pozadí, v rámci úlohy nebo zpětného volání, je nejspíš Nespuštěn na hlavním vlákně uživatelského rozhraní. V takovém případě byste měli zabalit kód do volání InvokeOnMainThread nebo BeginInvokeOnMainThread podobného:

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

InvokeOnMainThreadMetoda je definována NSObject tak, aby ji bylo možné volat z v rámci metod definovaných v jakémkoli objektu UIKit (například zobrazení nebo kontroler zobrazení).

Při ladění aplikací Xamarin. iOS bude vyvolána chyba, pokud se váš kód pokusí získat přístup k ovládacímu prvku uživatelského rozhraní z chybného vlákna. To vám pomůže tyto problémy sledovat a vyřešit pomocí metody InvokeOnMainThread. K tomu dochází pouze při ladění a nevyvolává chybu v sestavení vydaných verzí. Chybová zpráva se zobrazí takto:

Spuštění vlákna uživatelského rozhraní

Příklad vlákna na pozadí

Tady je příklad, který se pokusí získat přístup k ovládacímu prvku uživatelského rozhraní ( UILabel ) z vlákna na pozadí pomocí jednoduchého vlákna:

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

Tento kód vyvolá UIKitThreadAccessException při ladění. Chcete-li problém vyřešit (a zajistit, aby ovládací prvek uživatelského rozhraní byl přístupný pouze z hlavního vlákna uživatelského rozhraní), zabalte libovolný kód, který odkazuje na ovládací prvky uživatelského rozhraní uvnitř InvokeOnMainThread výrazu takto:

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

Nebudete je muset použít pro zbývající příklady v tomto dokumentu, ale je to důležitý koncept, který zapamatuje, kdy vaše aplikace vytváří síťové požadavky, používá centrum oznámení nebo jiné metody, které vyžadují obslužnou rutinu dokončení, která se spustí v jiném vlákně.

Příklad Async/await

Při použití klíčových slov Async/await v jazyce C# 5 není InvokeOnMainThread vyžadováno, protože když očekávaná úloha dokončí metodu, pokračuje v volajícím vlákně.

Tento ukázkový kód (který čeká na volání metody zpoždění, čistě pro demonstrační účely) zobrazuje asynchronní metodu, která je volána ve vlákně uživatelského rozhraní (Jedná se o obslužnou rutinu TouchUpInside). Vzhledem k tomu, že nadřazená metoda je volána ve vlákně uživatelského rozhraní, operace uživatelského rozhraní, jako je nastavení textu na UILabel nebo ukazující, UIAlertView lze bezpečně volat po dokončení asynchronních operací v vláknech na pozadí.

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";
}

Pokud je asynchronní metoda volána z vlákna na pozadí (ne z hlavního vlákna uživatelského rozhraní) InvokeOnMainThread , pak by se měla nadále vyžadovat.