応答性の高いアプリケーションの作成

応答性に優れた GUI を維持するための鍵の 1 つは長時間実行のタスクをバックグラウンド スレッドで実行することです。 ユーザーに表示する値を計算するとき、値の計算に 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 サービス呼び出しやインターネット データのダウンロードなど、バックグラウンドで実行できるあらゆる長時間実行タスクに使用できます。