Написание адаптивных приложений

Одним из ключей поддержания адаптивного графического интерфейса является выполнение длительных задач в фоновом потоке, чтобы графический интерфейс не был заблокирован. Предположим, что мы хотим вычислить значение для отображения пользователю, но это значение занимает 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";
    }
}

Теперь мы вычисляем значение в фоновом потоке, чтобы наш графический интерфейс оставался адаптивным во время вычисления. Однако при выполнении вычисления приложение завершает работу, оставляя это в журнале:

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

Это связано с тем, что необходимо обновить графический интерфейс из потока графического интерфейса. Наш код обновляет графический интерфейс из потока ThreadPool, что приводит к сбою приложения. Нам нужно вычислить наше значение в фоновом потоке, но затем выполнить обновление в потоке ГРАФИЧЕСКОго интерфейса, который обрабатывается с помощью 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");
    }
}

Этот код работает должным образом. Этот графический интерфейс остается адаптивным и правильно обновляется после выполнения вычисления.

Обратите внимание, что этот метод не просто используется для вычисления дорогого значения. Его можно использовать для любой длительной задачи, которая может выполняться в фоновом режиме, например вызов веб-службы или скачивание интернет-данных.