撰寫回應式應用程式Writing Responsive Applications

維護回應式 GUI 的其中一個關鍵,就是在背景執行緒上進行長時間執行的工作,因此 GUI 不會遭到封鎖。One of the keys to maintaining a responsive GUI is to do long-running tasks on a background thread so the GUI doesn't get blocked. 假設我們想要計算要向使用者顯示的值,但是該值需要5秒來計算:Let's say we want to calculate a value to display to the user, but that value takes 5 seconds to calculate:

public class ThreadDemo : Activity
{
    TextView textview;

    protected override void OnCreate (Bundle bundle)
    {
        base.OnCreate (bundle);

        // Create a new TextView and set it as our view
        textview = new TextView (this);
        textview.Text = "Working..";

        SetContentView (textview);

        SlowMethod ();
    }

    private void SlowMethod ()
    {
        Thread.Sleep (5000);
        textview.Text = "Method Complete";
    }
}

這會有效,但在計算值時,應用程式將會「停止回應」5秒。This will work, but the application will "hang" for 5 seconds while the value is calculated. 在這段期間,應用程式將不會回應任何使用者互動。During this time, the app will not respond to any user interaction. 為了解決這個情況,我們想要在背景執行緒上進行計算:To get around this, we want to do our calculations on a background thread:

public class ThreadDemo : Activity
{
    TextView textview;

    protected override void OnCreate (Bundle bundle)
    {
        base.OnCreate (bundle);

        // Create a new TextView and set it as our view
        textview = new TextView (this);
        textview.Text = "Working..";

        SetContentView (textview);

        ThreadPool.QueueUserWorkItem (o => SlowMethod ());
    }

    private void SlowMethod ()
    {
        Thread.Sleep (5000);
        textview.Text = "Method Complete";
    }
}

現在,我們會在背景執行緒上計算值,讓 GUI 在計算期間保持回應。Now we calculate the value on a background thread so our GUI stays responsive during the calculation. 不過,當計算完成時,我們的應用程式會當機,並將其保留在記錄檔中:However, when the calculation is done, our app crashes, leaving this in the log:

E/mono    (11207): EXCEPTION handling: Android.Util.AndroidRuntimeException: Exception of type 'Android.Util.AndroidRuntimeException' was thrown.
E/mono    (11207):
E/mono    (11207): Unhandled Exception: Android.Util.AndroidRuntimeException: Exception of type 'Android.Util.AndroidRuntimeException' was thrown.
E/mono    (11207):   at Android.Runtime.JNIEnv.CallVoidMethod (IntPtr jobject, IntPtr jmethod, Android.Runtime.JValue[] parms)
E/mono    (11207):   at Android.Widget.TextView.set_Text (IEnumerable`1 value)
E/mono    (11207):   at MonoDroidDebugging.Activity1.SlowMethod ()

這是因為您必須從 GUI 執行緒更新 GUI。This is because you must update the GUI from the GUI thread. 我們的程式碼會從 ThreadPool 執行緒更新 GUI,導致應用程式損毀。Our code updates the GUI from the ThreadPool thread, causing the app to crash. 我們需要在背景執行緒上計算值,但在 GUI 執行緒上進行更新時,會使用RunOnUIThread進行處理:We need to calculate our value on the background thread, but then do our update on the GUI thread, which is handled with Activity.RunOnUIThread:

public class ThreadDemo : Activity
{
    TextView textview;

    protected override void OnCreate (Bundle bundle)
    {
        base.OnCreate (bundle);

        // Create a new TextView and set it as our view
        textview = new TextView (this);
        textview.Text = "Working..";

        SetContentView (textview);

        ThreadPool.QueueUserWorkItem (o => SlowMethod ());
    }

    private void SlowMethod ()
    {
        Thread.Sleep (5000);
        RunOnUiThread (() => textview.Text = "Method Complete");
    }
}

此程式碼會如預期般運作。This code works as expected. 下限計算之後,這個 GUI 會保持回應,並適當地更新。This GUI stays responsive and gets properly updated once the calculation is comple.

請注意,這項技術不是用來計算昂貴的價值。Note this technique isn't just used for calculating an expensive value. 它可用於任何長時間執行的工作,可在背景中完成,例如 web 服務呼叫或下載網際網路資料。It can be used for any long-running task that can be done in the background, like a web service call or downloading internet data.