Под фоновыми задачами подразумевается способ запуска задачи в фоновом режиме вне жизненного цикла мобильного приложения. Это отличается от выполнения их в фоновом потоке, и в некоторых случаях запуск осуществляется в совершенно другом процессе. Фоновые задачи позволяют запускать код через определенные промежутки времени или делать так, чтобы задачи продолжали выполняться даже тогда, когда приложение закрыто, что бывает полезно в таких ситуациях, как, например, при загрузке больших файлов. У каждой платформы есть собственный способ обработки фоновых задач. Мы также рассмотрим, как взаимодействовать с ними в Xamarin Forms.
На случай если Вы захотите рассмотреть полный пример кода, я также создал репозиторий на GitHub BackgroundTasks.
Android Services
Для запуска фоновой задачи в Android используется Service. В Android задачи обычно классифицируются на те, что с длительным временем выполнения и периодические (Long Running Tasks и Periodic Tasks). Любая из них является подходящим основанием для создания Service. Тот тип службы, который требуется создать, зависит от вида ее использования, однако, в данном примере мы рассмотрим запущенную службу (Started Service). Запущенная служба может быть вызвана через внешнее событие или действие. Эта служба будет продолжаться, пока не будет остановлена, — независимо от состояния приложения.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | [Service] public class PeriodicService : Service { public override IBinder OnBind(Intent intent) { return null; } public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId) { // From shared code or in your PCL return StartCommandResult.NotSticky; } } |
Эту службу можно запустить в коде Android следующим образом:
1 2 | <span class="token keyword">var</span> intent <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Intent</span> <span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">,</span> <span class="token keyword">typeof</span><span class="token punctuation">(Periodic</span>Service<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">; StartService(intent);</span> |
Или можно использовать Alarm Manager, чтобы вызывать ее периодически.
Alarm Manager
Если требуется периодическая фоновая задача, Alarm Manager обеспечивает удобный способ запуска службы через определенные промежутки времени. Дня настойки приложения на то, чтобы оно периодически вызывалось необходимо добавить следующий код:
Создание BroadcastReceiver
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | [BroadcastReceiver] public class BackgroundReceiver : BroadcastReceiver { public override void OnReceive(Context context, Intent intent) { PowerManager pm = (PowerManager)context.GetSystemService(Context.PowerService); PowerManager.WakeLock wakeLock = pm.NewWakeLock(WakeLockFlags.Partial, "BackgroundReceiver"); wakeLock.Acquire(); // Run your code here wakeLock.Release(); } } |
Фоновые задачи в iOS
Для получения более подробной информации о каждой из них ознакомьтесь с документацией Xamarin в iOS Backgrounding. В этом примере будет использоваться фоновая выборка (Background Fetch).
Для начала активизируйте фоновую выборку. Перейдите к Properties > iOS Application и Background Modes, чтобы активировать фоновую выборку (Background Fetch).
Затем в AppDelegate.cs у FinishedLaunching добавьте в следующую строку:
1 2 3 4 5 6 7 8 9 | public override bool FinishedLaunching(UIApplication app, NSDictionary options) { global::Xamarin.Forms.Forms.Init(); LoadApplication(new App()); UIApplication.SharedApplication.SetMinimumBackgroundFetchInterval(UIApplication.BackgroundFetchIntervalMinimum); return base.FinishedLaunching(app, options); } |
Затем переопределите PerformFetch, чтобы выполнить код для каждой выборки.
1 2 3 4 5 6 7 8 | public override void PerformFetch(UIApplication application, Action<UIBackgroundFetchResult> completionHandler) { // Check for new data, and display it // Inform system of fetch results completionHandler(UIBackgroundFetchResult.NewData); } |
Если требуется имитировать фоновую выборку, то в настоящее время с помощью Visual Studio в Windows это сделать невозможно. Это можно сделать с помощью Mac Build Host.
Фоновые задачи UWP
Для фоновых задач в UWP необходимо создать новый компонент среды выполнения.
Добавьте ссылку на этот новый компонент из приложения UWP.
Добавьте фоновую задачу к компоненту.
1 2 3 4 5 6 7 8 9 10 11 12 | public sealed class ExampleBackgroundTask : IBackgroundTask { BackgroundTaskDeferral _deferral; public void Run(IBackgroundTaskInstance taskInstance) { _deferral = taskInstance.GetDeferral(); // Run your background task code here _deferral.Complete(); } } |
Теперь надо убедиться в том, что мы зарегистрировали фоновую задачу в appxmanifest.
Наконец, необходимо запустить фоновую задачу. В приложении UWP создайте и зарегистрируйте фоновую задачу.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | private async Task BackgroundTask() { BackgroundExecutionManager.RemoveAccess(); await BackgroundExecutionManager.RequestAccessAsync(); var builder = new BackgroundTaskBuilder(); builder.Name = "BackgroundTask"; builder.TaskEntryPoint = "UWPRuntimeComponent.BackgroundTask"; builder.SetTrigger(new SystemTrigger(SystemTriggerType.InternetAvailable, false)); BackgroundTaskRegistration task = builder.Register(); task.Completed += Task_Completed; } private void Task_Completed(BackgroundTaskRegistration sender, BackgroundTaskCompletedEventArgs args) { var settings = Windows.Storage.ApplicationData.Current.LocalSettings; var key = "BackgroundTask"; var message = settings.Values[key].ToString(); // Run your background task code here MessagingCenter.Send<object, string>(this, "UpdateLabel", message); } |
Кроме того, необходимо проверить, зарегистрирована ли она, прежде чем пытаться ее зарегистрировать снова, и при необходимости отменить регистрацию. Это можно сделать с помощью следующего кода:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | private void Deregister() { var taskName = "BackgroundTask"; foreach (var task in BackgroundTaskRegistration.AllTasks) if (task.Value.Name == taskName) task.Value.Unregister(true); } private bool IsRegistered() { var taskName = "BackgroundTask"; foreach (var task in BackgroundTaskRegistration.AllTasks) if (task.Value.Name == taskName) return true; return false; } |
Интеграция с Xamarin Forms
Как видите, каждая платформа имеет разительные отличия в том, как она обрабатывает фоновые задачи. Если вы хотите вызывать их или интегрировать с Xamarin Forms, тогда необходимо создать определенный уровень абстракции, чтобы запустить службу, если она необходима, и получать обратные сообщения.
При помощи такого интерфейса, как показан ниже, можно ввести ее в приложение Xamarin Forms, чтобы запускать службу при необходимости.
1 2 3 4 | public interface IBackgroundService { void Start(); } |
Однако это один из тех редких случаев, когда я все-таки рекомендую MessagingCenter в качестве возможной альтернативы. Но только при получении обратной информации, а не для запуска службы. Например, в Service, AppDelegate или BackgroundTask добавьте этот код:
1 | MessagingCenter.Send<object, string>(this, "UpdateLabel", "Hello from [Platform]"); |
И затем подпишитесь на него в приложении Xamarin Forms.
1 2 3 4 5 6 | MessagingCenter.Subscribe<object, string>(this, "UpdateLabel", (s,e) => { Device.BeginInvokeOnMainThread(() => { BackgroundServiceLabel.Text = e; }); }); |
Конфликты ресурсов
В качестве предостережения следует напомнить, что служба или приложение обращается, например, к базе данных SQLite. Два процесса не могут получать доступ к файлу одновременно, если только не для чтения (read-only). Таким образом, убедитесь, что только один процесс выполняет действия над файлом (файлами) или заблокированными ресурсами.
Автор: Adam Pedley
Источник: XamarinHelp.com
1 Comment