共用方式為


在 Xamarin.iOS 中使用 UI 線程

應用程式使用者介面一律為單個線程,即使是在多線程裝置中, 畫面上只有一個表示法,而且任何顯示內容的變更都需要透過單一「存取點」協調。 這可防止多個線程同時嘗試更新相同的圖元(例如)。

您的程式代碼應該只對主要 (或 UI) 線程的使用者介面控件進行變更。 在不同線程上發生的任何UI更新(例如回呼或背景線程)都可能不會轉譯到畫面,甚至可能會導致當機。

UI 線程執行

當您在檢視中建立控件,或處理使用者起始的事件,例如觸控時,程式代碼已在UI線程的內容中執行。

如果程式代碼是在背景線程上執行,在工作或回呼中,它很可能不會在主要 UI 線程上執行。 在此情況下,您應該將程式代碼包裝在對的呼叫 InvokeOnMainThread 中,或 BeginInvokeOnMainThread 如下所示:

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

方法 InvokeOnMainThread 已定義於 上 NSObject ,因此可以從任何UIKit對象上定義的方法內呼叫它(例如 View 或 View Controller)。

偵錯 Xamarin.iOS 應用程式時,如果您的程式代碼嘗試從錯誤的線程存取 UI 控制件,將會擲回錯誤。 這可協助您追蹤並修正 InvokeOnMainThread 方法的這些問題。 這隻會在偵錯時發生,而且不會在發行組建中擲回錯誤。 錯誤訊息會顯示如下:

UI 線程執行

背景線程範例

以下是使用簡單線程從背景線程存取使用者介面控件 (a UILabel) 的範例:

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

該程式代碼會在偵錯時擲回 UIKitThreadAccessException 。 若要修正問題(並確保使用者介面控件只能從主要 UI 線程存取),請將任何參考 UI 控制件的程式代碼包裝在運算式內 InvokeOnMainThread ,如下所示:

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

您不需要針對本檔中的其餘範例使用此專案,但當您的應用程式提出網路要求時,請使用通知中心或其他需要完成處理程式的方法,以在另一個線程上執行,這是一個重要的概念。

Async/Await 範例

使用 C# 5 async/await 關鍵詞時並非必要, InvokeOnMainThread 因為當等候的工作完成方法時,會在呼叫線程上繼續。

這個範例程式代碼(在 Delay 方法呼叫上等候,純粹是為了示範目的)會顯示在 UI 線程上呼叫的異步方法(它是 TouchUpInside 處理程式)。 由於在UI線程上呼叫包含的方法,因此在背景線程上完成異步操作之後,可以在上 UILabel 設定文字或顯示 UIAlertView 的文字等UI作業安全地呼叫。

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

如果從背景線程呼叫異步方法(不是主要 UI 線程), InvokeOnMainThread 則仍然需要 。