Android, исключение Async/Await после навигации по активности

Я пишу приложение для Android в Xamarin, которое заполняет список данными из HttpClient. Есть несколько экранов.

Вот проблема:

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

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

Я пытался использовать токены отмены, поэтому, когда они уходят, я вызываю Cancel для CancelTokenSource. Возможно, я делаю это неправильно, вы должны ждать, пока не получите подтверждение об отмене асинхронного вызова, прежде чем продолжить каким-либо образом?

Как правильно предотвратить эту проблему?

КОД:

во фрагменте:

private async void SwipeRefreshLayout_Refresh(object sender, EventArgs e)
{
    _cancelRefreshCall = new CancellationTokenSource();
    var swipeRefreshLayout = View.FindViewById<SwipeRefreshLayout>(Resource.Id.swipeRefreshLayout1);
    swipeRefreshLayout.Refreshing = true;
    _items.Clear();
    List<item> items = new List<item>();

    try
    {
        await Task.Run(async () =>
        {
            try
            {
                await _itemService.RefreshItems(_cancelRefreshCall.Token);
                restaurants = await _itemService.GetLatestItems(_cancelRefreshCall.Token);
            }
            catch (System.OperationCanceledException ex)
            {
            }
            catch (Exception ex)
            {

            }
        }, _cancelRefreshCall.Token);
    }
    catch (Exception ex)
    {

    }

    foreach (var item in items)
    {
        _items.Add(item);
    }

    swipeRefreshLayout.Refreshing = false;
}

Вот кнопка, чтобы уйти:

    private void SubmitItem_Click(object sender, EventArgs e)
    {
        if (_cancelRefreshCall != null)
        {
            _cancelRefreshCall.Cancel();
        }

        // redirect to preference page once done
        var fgManager = this.Activity.SupportFragmentManager.BeginTransaction();
        var fragment = this.Activity.SupportFragmentManager.FindFragmentByTag(Constants.LeahNutrinoPrefTag);

        if (fragment== null)
        {
            fgManager.Replace(Resource.Id.HomeFrameLayout, new ProfileFragment(), Constants.ProfileTag);
        }
        else
        {
            fgManager.Replace(Resource.Id.HomeFrameLayout, fragment, Constants.ProfileTag);
        }

        fgManager.Commit();
    }

person GMorini    schedule 11.08.2017    source источник
comment
Можете ли вы предоставить несколько примеров кода в вопросе о том, что вы пробовали?   -  person David Oliver    schedule 11.08.2017
comment
Готово, извините за это.   -  person GMorini    schedule 11.08.2017


Ответы (1)


Может быть, дело в том, что вы закрываете поле _cancelRefreshCall? Всякий раз, когда вы присваиваете ему новое значение, закрытие в выполняющейся задаче будет указывать на новый, неотмененный CancellationTokenSource.

Вместо этого вы можете попробовать следующий код:

private async void SwipeRefreshLayout_Refresh(object sender, EventArgs e)
{
    _cancelRefreshCall?.Cancel();
    _cancelRefreshCall = new CancellationTokenSource();
    var token = _cancelRefreshCall.Token;
    var swipeRefreshLayout = View.FindViewById<SwipeRefreshLayout>(Resource.Id.swipeRefreshLayout1);
    swipeRefreshLayout.Refreshing = true;
    _items.Clear();
    List<item> items = new List<item>();

    try
    {
        await Task.Run(async () =>
        {
            try
            {
                await _itemService.RefreshItems(token);
                restaurants = await _itemService.GetLatestItems(token);
            }
            catch (System.OperationCanceledException ex)
            {
            }
            catch (Exception ex)
            {

            }
        }, token);
    }
    catch (Exception ex)
    {

    }

    foreach (var item in items)
    {
        _items.Add(item);
    }

    swipeRefreshLayout.Refreshing = false;
person David Oliver    schedule 12.08.2017
comment
Я попробую это. Я реализовал временное решение. Кнопка, которая перемещается, когда ее нажимают, все, что она делает, это отменяет источник токена. Я знаю, что это вызовет исключение OperationCanceledException, поэтому я перехватываю его и ухожу в блоке перехвата. Перехват исключения занимает много времени. Это означает, что пользователь не будет перемещен на другой экран, пока у нас не будет подтверждения, что задача была отменена. Итак, похоже, что загрузка экрана занимает много времени, но мы просто ждем отмены задачи. Базовым отмененным методом является sendAsync. Почему так медленно? - person GMorini; 12.08.2017
comment
Должен ли это быть сервис для Android? Кроме того, эта проблема возникла еще до того, как у меня появились какие-либо токены отмены, поэтому я скептически отношусь к тому, что это проблема. Я попробую ваше решение, хотя. - person GMorini; 12.08.2017