Фоновые задачи в Xamarin

110
14 февраля 2021, 03:50

Попытался реализовать работу фоновой задачи в Android и iOS как это описано в статье.

Для этого в MainPage я создал Label с именем ticker и в код формы добавил следующий код:

В конструкторе вызываю метод HandleReceivedMessages();, который выглядит следующим образом:

void HandleReceivedMessages()
{
    MessagingCenter.Subscribe<TickedMessage>(this, "TickedMessage", message =>
    {
        Device.BeginInvokeOnMainThread(() =>
        {
            try
            {
                ticker.Text = message.Message;
            }
            catch { }
        });
    });
    MessagingCenter.Subscribe<CancelledMessage>(this, "CancelledMessage", message =>
    {
        Device.BeginInvokeOnMainThread(() =>
        {
            try
            {
                ticker.Text = "Cancelled";
            }
            catch { }
        });
    });
}

При нажатии на кнопку запуска задачи выполняется следующий код:

var message = new StartLongRunningTaskMessage();
MessagingCenter.Send(message, "StartLongRunningTaskMessage");

А при нажатие на кнопки остановки следующий:

var message = new StopLongRunningTaskMessage();
MessagingCenter.Send(message, "StopLongRunningTaskMessage");

Также я определил несколько вспомогательных классов:

public class StartLongRunningTaskMessage { }
public class StopLongRunningTaskMessage { }
public class TickedMessage
{
    public string Message { get; set; }
}
public class CancelledMessage { }

И класс с логикой фоновой задачи:

public class TaskCounter
{
    public async Task RunCounter(CancellationToken token)
    {
        await Task.Run(async () =>
        {
            for (long i = 0; i < long.MaxValue; i++)
            {
                token.ThrowIfCancellationRequested();
                await Task.Delay(250);
                TickedMessage message = new TickedMessage {Message = i.ToString()};
                Device.BeginInvokeOnMainThread(() => { MessagingCenter.Send(message, "TickedMessage"); });
            }
        }, token);
    }
}

В Android-проекте в MainActivity.cs в метод OnCreate я добавил следующий код:

MessagingCenter.Subscribe<StartLongRunningTaskMessage> (this, "StartLongRunningTaskMessage", message => {
    var intent = new Intent (this, typeof(LongRunningTaskService));
    StartService (intent);
});
MessagingCenter.Subscribe<StopLongRunningTaskMessage> (this, "StopLongRunningTaskMessage", message => {
    var intent = new Intent (this, typeof(LongRunningTaskService));
    StopService (intent);
});

А также определил сервис с несколькими методами:

[Service]
public class LongRunningTaskService : Service
{
    CancellationTokenSource _cts;
    public override IBinder OnBind(Intent intent)
    {
        return null;
    }
    public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
    {
        _cts = new CancellationTokenSource();
        Task.Run(() =>
        {
            try
            {
                //INVOKE THE SHARED CODE
                TaskCounter counter = new TaskCounter();
                counter.RunCounter(_cts.Token).Wait();
            }
            catch (System.OperationCanceledException) { }
            finally
            {
                if (_cts.IsCancellationRequested)
                {
                    var message = new CancelledMessage();
                    Device.BeginInvokeOnMainThread(() => MessagingCenter.Send(message, "CancelledMessage"));
                }
            }
        }, _cts.Token);
        return StartCommandResult.Sticky;
    }
    public override void OnDestroy()
    {
        if (_cts != null)
        {
            _cts.Token.ThrowIfCancellationRequested();
            _cts.Cancel();
        }
        base.OnDestroy();
    }
}

В iOS-проекте в AppDelegate.cs в метод FinishedLaunching добавил следующий код:

MessagingCenter.Subscribe<StartLongRunningTaskMessage>(this, "StartLongRunningTaskMessage", async message =>
{
    _longRunningTaskExample = new iOSLongRunningTaskExample();
    await _longRunningTaskExample.Start();
});
MessagingCenter.Subscribe<StopLongRunningTaskMessage>(this, "StopLongRunningTaskMessage",
    message => { _longRunningTaskExample.Stop(); });

И также определил класс:

public class iOSLongRunningTaskExample
{
    nint _taskId;
    CancellationTokenSource _cts;
    public async Task Start()
    {
        _cts = new CancellationTokenSource();
        _taskId = UIApplication.SharedApplication.BeginBackgroundTask("LongRunningTask", OnExpiration);
        try
        {
            //INVOKE THE SHARED CODE
            TaskCounter counter = new TaskCounter();
            await counter.RunCounter(_cts.Token);
        }
        catch (OperationCanceledException) { }
        finally
        {
            if (_cts.IsCancellationRequested)
            {
                var message = new CancelledMessage();
                Device.BeginInvokeOnMainThread(() => MessagingCenter.Send(message, "CancelledMessage"));
            }
        }
        UIApplication.SharedApplication.EndBackgroundTask(_taskId);
    }
    public void Stop()
    {
        _cts.Cancel();
    }
    void OnExpiration()
    {
        _cts.Cancel();
    }
}

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

Answer 1

По умолчанию служба запускается в том же процессе, что и приложение Android. Можно запустить службу в собственном процессе, задав для свойства ServiceAttribute.IsolatedProcess значение true

https://docs.microsoft.com/ru-ru/xamarin/android/app-fundamentals/services/creating-a-service/

READ ALSO
Uri типа &ldquo;pack&rdquo; в Wpf

Uri типа “pack” в Wpf

У меня есть консольное приложение с подключённой сборкой PresentationCore(тесты делаются здесь - так проще; проверял в оконном приложении и результаты...

110
Проблема публикации файлов по FTP (VS2019)

Проблема публикации файлов по FTP (VS2019)

Публикую Web проект (ASPNET Core 2

117
Как задать координаты кнопки WPF? (XAML)

Как задать координаты кнопки WPF? (XAML)

Мне нужно создать кнопку, делаю так:

232
Помогите разобраться с If [закрыт]

Помогите разобраться с If [закрыт]

Хотите улучшить этот вопрос? Обновите вопрос так, чтобы он вписывался в тематику Stack Overflow на русском

105