Escritura de aplicaciones con capacidad de respuesta

Una de las claves para mantener una GUI con capacidad de respuesta es realizar tareas de ejecución prolongada en un subproceso en segundo plano para que la GUI no se bloquee. Supongamos que queremos calcular un valor para mostrar al usuario, pero ese valor tarda cinco segundos en calcular:

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

Esto funcionará, pero la aplicación se "bloqueará" durante 5 segundos mientras se calcula el valor. Durante este tiempo, la aplicación no responderá a ninguna interacción del usuario. Para solucionar esto, queremos realizar nuestros cálculos en un subproceso en segundo plano:

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

Ahora calculamos el valor en un subproceso en segundo plano para que nuestra GUI siga respondiendo durante el cálculo. Sin embargo, cuando se realiza el cálculo, la aplicación se bloquea, dejando esto en el registro:

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 ()

Esto se debe a que debe actualizar la GUI desde el subproceso de GUI. Nuestro código actualiza la GUI desde el subproceso ThreadPool, lo que hace que la aplicación se bloquee. Necesitamos calcular nuestro valor en el subproceso en segundo plano, pero, a continuación, realizar nuestra actualización en el subproceso de gui, que se controla con 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");
    }
}

Este código funciona según lo previsto. Esta GUI sigue respondiendo y se actualiza correctamente una vez que el cálculo se complete.

Tenga en cuenta que esta técnica no solo se usa para calcular un valor costoso. Se puede usar para cualquier tarea de larga duración que se pueda realizar en segundo plano, como una llamada de servicio web o descargar datos de Internet.