当应用程序进入后台(即进入睡眠状态)时,Xamarin Forms运行代码

时间:2020-06-12 19:42:39

标签: c# ios xamarin.forms uwp background-process

我有一个应用程序(iOS,Android,UWP),每分钟检索一次数据。当应用程序处于前台时,我使用计时器启动调用以检索数据。 当应用程序进入睡眠状态时,我想停止使用计时器来检索数据,并使用将在后台运行并在应用程序处于睡眠状态的设备保持运行状态。 (如果应用终止,则停止也可以。 此应用程序不需要互联网连接,因为该应用程序所连接的本地网络中的数据源都是如此。看来我使用以下代码运行了iOS和Android。我现在面临的挑战是如何使UWP版本继续运行。

共享代码项目中的代码

接口代码(我在一个单独的文件中)。

using System;
using System.Threading.Tasks;

namespace DependencyServiceDemos
{
    public interface IBackgroundService
    {
        Task RunCodeInBackgroundMode(Func<Task> action, string name = "BackgroundService");
    }
}

这是将在后台执行的代码。

using System;
using System.Diagnostics;
using System.Timers;
using Xamarin.Forms;

namespace DependencyServiceDemos
{
    public static class BackgroundCode
    {
        public static bool StopRunning { get; set; } = false;

        public static Timer iobj_timer;

        public static bool GetBackgroundData(int pi_SecondsLoop)
        {
            bool lb_ReturnValue = true;


            // Start a timer that runs after 30 seconds.
            Device.StartTimer(TimeSpan.FromSeconds(pi_SecondsLoop), () =>
            {
                // Do the actual request and wait for it to finish.
                App.GlobalBackgroundData.MyBackgroundData = "Current Date / Time: " + DateTime.Now.ToString();

                // Don't repeat the timer (we will start a new timer when the request is finished)
                if (StopRunning)
                {
                    lb_ReturnValue = false;
                    Debug.WriteLine("Stopping Background Process: " + App.GlobalBackgroundData.MyBackgroundData);
                }
                else
                {
                    Debug.WriteLine("Background Code Processed: " + App.GlobalBackgroundData.MyBackgroundData);
                }

                return lb_ReturnValue;
            });

            return lb_ReturnValue;
        }
    }
}

这是ViewModel,我在其中更新在后台收集的数据以更新UI。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Text;

namespace DependencyServiceDemos
{
    public class BackgroundData : INotifyPropertyChanged
    {
        private string is_BackgroundData = "";
        public string MyBackgroundData
        {
            get
            {
                return is_BackgroundData;
            }
            set
            {
                is_BackgroundData = value;
                OnPropertyChanged(); 
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

在App.xaml.cs中,我声明了ViewModel的静态实例,如下所示:

public static BackgroundData GlobalBackgroundData { get; set; }

然后我在应用程序构造函数中初始化ViewModel:

InitializeComponent();
GlobalBackgroundData = new BackgroundData();

在iOS应用程序中-我实现的界面如下:

    using DependencyServiceDemos.iOS;
    using DependencyServiceDemos;
    using System;
    using System.Threading.Tasks;
    using UIKit;
    using Xamarin.Forms;

    [assembly: Dependency(typeof(BackgroundService))]
    namespace DependencyServiceDemos.iOS 
    {
        public class BackgroundService : IBackgroundService
        {
            public async Task RunCodeInBackgroundMode(Func<Task> action, string name = "BackgroundService")
            {
                nint taskId = 0;
                var taskEnded = false;
                taskId = UIApplication.SharedApplication.BeginBackgroundTask(name, () =>
                {
                    //when time is up and task has not finished, call this method to finish the task to prevent the app from being terminated
                    Console.WriteLine($"Background task '{name}' got killed");
                    taskEnded = true;
                    UIApplication.SharedApplication.EndBackgroundTask(taskId);
                });
                await Task.Factory.StartNew(async () =>
                {
                    //here we run the actual task
                    Console.WriteLine($"Background task '{name}' started");
                    await action();
                    taskEnded = true;
                    UIApplication.SharedApplication.EndBackgroundTask(taskId);
                    Console.WriteLine($"Background task '{name}' finished");
                });

                await Task.Factory.StartNew(async () =>
                {
                    //Just a method that logs how much time we have remaining. Usually a background task has around 180 seconds to complete. 
                    while (!taskEnded)
                    {
                        Console.WriteLine($"Background task '{name}' time remaining: {UIApplication.SharedApplication.BackgroundTimeRemaining}");
                        await Task.Delay(1000);
                    }
                });
            }

        }
    }

我更新了FinishLaunching以注册依赖项服务,如下所示:

        public override bool FinishedLaunching(UIApplication app, NSDictionary options)
        {
            global::Xamarin.Forms.Forms.Init();
            LoadApplication(new App());
            DependencyService.Register<IBackgroundService, BackgroundService>();
            return base.FinishedLaunching(app, options);
        }

在Android项目中,我实现了如下界面:

using Android.Content;
using Android.OS;
using DependencyServiceDemos;
using System;
using System.Diagnostics;
using System.Threading.Tasks;
using Xamarin.Forms;

[assembly: Dependency(typeof(IBackgroundService))]
namespace DependencyServiceDemos.Droid
{
    public class BackgroundService : IBackgroundService
    {
        public async Task RunCodeInBackgroundMode(Func<Task> action, string name = "BackgroundService")
        {
            var powerManager = (PowerManager)Android.App.Application.Context.GetSystemService(Context.PowerService);
            var wakeLock = powerManager.NewWakeLock(WakeLockFlags.Partial,
                                                    name);
            //acquire a partial wakelock. This prevents the phone from going to sleep as long as it is not released.
            wakeLock.Acquire();
            var taskEnded = false;

            await Task.Factory.StartNew(async () =>
            {
                //here we run the actual code
                Console.WriteLine($"Background task '{name}' started");
                await action();
                Console.WriteLine($"Background task '{name}' finished");
                wakeLock.Release();
                taskEnded = true;
            });

            await Task.Factory.StartNew(async () =>
            {
                //just a method to keep track of how long the task runs
                var stopwatch = new Stopwatch();
                stopwatch.Start();
                while (!taskEnded)
                {
                    Console.WriteLine($"Background '{name}' task with wakelock still running ({stopwatch.Elapsed.TotalSeconds} seconds)");
                    await Task.Delay(1000);
                }
                stopwatch.Stop();
            });
        }
    }
}

然后,我在MainActivity OnCreated事件中将依赖项服务注册为带有以下代码的最后一行:

DependencyService.Register<IBackgroundService, BackgroundService>();

一切似乎都很好。现在,我尝试使用以下代码在UWP中实现该接口:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DependencyServiceDemos.UWP.Services
{
    public class BackgroundService : IBackgroundService
    {
        public async Task RunCodeInBackgroundMode(Func<Task> action, string name = "BackgroundService")
        {
            await Task.Factory.StartNew(async () =>
            {
                //here we run the actual code
                Console.WriteLine($"Background task '{name}' started");
                await action();
                Console.WriteLine($"Background task '{name}' finished");
            });
        }
    }
}

我还在UWP项目的MainPage构造函数中初始化服务,如下所示:

DependencyService.Register<IBackgroundService, BackgroundService>();

因此,据我所知,该服务中的代码已执行,但是以下代码行似乎并未在共享代码的静态类中执行BackgroundCode.GetBackgroundData。

两件事: 1.我的iOS或Android实施是否有任何“错误”,可能会被相应的商店踢走? 2.知道为什么我的UWP代码不起作用吗?

await action();

0 个答案:

没有答案