асинхронный метод останавливает интерфейс - PullRequest
0 голосов
/ 02 мая 2018

Ожидаемый результат :

TestAsync вызывается потоком пользовательского интерфейса, а рабочий поток выполняет LongTask.

Фактический результат :

Поток пользовательского интерфейса выполняет все

Тест

public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
    // [...]

    _fab = root.FindViewById<FloatingActionButton>(...);
    _fab.Click += ((sender, v) =>  TestAsync("fab"));

    // [...]
}

private async void TestAsync(string origin)
{
    await LongTask(); 
}

private async Task LongTask()
{
    while (true) { } // Thread should hung here
}

Итог: пользовательский интерфейс зависает.

Тест 2 : Чтобы убедиться, что пользовательский интерфейс выполняет все, я сделал сетевую операцию (что не разрешено в потоке пользовательского интерфейса в Android)

public async Task<int> Network(string s)
{
    URL url = new URL("http://www.randomtext.me/api/");
    Java.IO.BufferedReader reader = new Java.IO.BufferedReader(new Java.IO.InputStreamReader(url.OpenStream()));

    int count = 0;
    string str;
    while ((str = reader.ReadLine()) != null) {
        count += str.Length;
    }
    reader.Close();

    await Task.Delay(3000); // To make sure this method is compiled as async even though it isn't necessary

    return count;
}

Итог: NetworkOnMainThreadException.

Вопрос:

Почему методы LongTask и Network не выполняются в рабочем потоке? Каковы await / async на то время?

Спасибо.

Ответы [ 3 ]

0 голосов
/ 02 мая 2018

Основное правило:

  • Связанное с пользовательским интерфейсом поведение должно быть в основном потоке (пользовательском потоке)
  • сетевые / тяжелые задачи должны выполняться в неосновном потоке (ui).

Вы пытаетесь выполнить сетевую задачу из основного потока (пользовательского интерфейса), это вызвало у вас это исключение.

Попробуйте создать класс TestAsync, расширяющий класс AsyncTask. Затем переопределите методы doInBackground, onPostExecute.

  • Создайте свою сетевую логику в методе doInBackground.
  • Затем обновите свой интерфейс в методе onPostExecute.

Ссылка на это: http://programmerguru.com/android-tutorial/what-is-asynctask-in-android/

0 голосов
/ 02 мая 2018

Задача первая: асинхронное / ожидание

private async void TestAsync(string origin)
{
    await LongTask(); 
}

Пояснение:

Когда вызывается делегат события нажатия кнопки, он вызывается из главного диспетчера (поток пользовательского интерфейса). Он говорит делегату о необходимости вызова TestAsync("Fab") Синхронно. Когда делегат проходит через тестовый асинхронный метод, ему предписывают запустить задачу LongTask, но вы также говорите ему дождаться результата этой задачи с помощью запроса 'await'. Таким образом, метод TestAsync не может завершиться, пока не будет завершен LongTask; так как это запрашивается у главного диспетчера, остальная часть пользовательского интерфейса зависает до его завершения.

Разрешение:

public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
    // [...]

    _fab = root.FindViewById<FloatingActionButton>(...);
    _fab.Click += ((sender, v) =>  TestAsync("fab"));

    // [...]
}

private async void TestAsync(string origin)
{
    // By not calling await you tell the code that you don't need this
    // calling method to await a return value.
    // using Task.Run explicitly requests another thread process this request
    Task.Run(LongTask); 
}

private async Task LongTask()
{
    while (true) { } // Thread should hung here
}


Задача вторая: Сеть

public async Task<int> Network(string s)
{
    URL url = new URL("http://www.randomtext.me/api/");
    Java.IO.BufferedReader reader = new Java.IO.BufferedReader(new Java.IO.InputStreamReader(url.OpenStream()));

    int count = 0;
    string str;
    while ((str = reader.ReadLine()) != null) {
        count += str.Length;
    }
    reader.Close();

    await Task.Delay(3000); // To make sure this method is compiled as async even though it isn't necessary

    return count;
}

Пояснение:

Как и прежде, это во многом зависит от того, как запускается задача, см. Эту цитату из документации Microsoft:

Ключевые слова async и await не приводят к созданию дополнительных потоков. создано. Асинхронные методы не требуют многопоточности, потому что асинхронные метод не работает в своем собственном потоке. Метод работает на текущем контекст синхронизации и использует время в потоке, только когда метод активен. Вы можете использовать Task.Run для перемещения работы с процессором в фоновый поток, но фоновый поток не помогает с процессом это только ожидание результатов, чтобы стать доступными.

Я надеюсь, что этот ответ добавляет немного больше деталей, которые будут использоваться наряду с другими ответами на ваш вопрос.

0 голосов
/ 02 мая 2018

и рабочий поток выполняет LongTask.

Нет, этого не произойдет само собой. Вы ждете от потока GUI, и поэтому вы заблокируете его. Этот шаблон подходит для асинхронного ввода-вывода, потому что это освободит поток.

Но когда ваш случай связан с процессором, для async / await нет смысла, используйте Task.Run:

private void TestAsync(string origin)
{
    Task.Run( LongTask); 
}
...