编写响应式应用程序

维护响应式 GUI 的关键之一是在后台线程上执行长时间运行的任务,这样 GUI 就不会被阻止。 假设我们要计算要显示给用户的值,但该值需要 5 秒才能计算:

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 秒。 在此期间,应用不会响应任何用户交互。 为了解决此问题,我们希望在后台线程上执行计算:

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 在计算过程中保持响应。 但是,计算完成后,应用会崩溃,并将此保留在日志中:

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。 我们的代码从 ThreadPool 线程更新 GUI,导致应用崩溃。 我们需要在后台线程上计算值,但在 GUI 线程上执行更新,该线程由 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");
    }
}

此代码按预期工作。 计算完成后,此 GUI 会保持响应并得到正确更新。

请注意,此方法不仅用于计算昂贵的值。 它可用于可在后台执行的任何长时间运行的任务,例如 Web 服务调用或下载 Internet 数据。