Xamarin Forms:即使应用程序在后台,如何每 x 秒 运行 一个服务?

Xamarin Forms: How to run a service every x seconds even the app is in background?

我使用以下代码每 10 秒调用一个函数。

var startTimeSpan = TimeSpan.Zero;
var periodTimeSpan = TimeSpan.FromSeconds(10);

var timer = new System.Threading.Timer((e) =>
{
    MyFunction();
}, null, startTimeSpan, periodTimeSpan);

MyFunction() 将在应用程序处于打开状态时每 10 秒执行一次。当应用程序在后台时,该功能不会被调用。

那么如何在应用程序处于后台时调用函数?是否有用于此的任何 NuGet 包,或者我们需要使用依赖服务来完成此操作?

更新

当我运行使用你的演示时,我得到以下异常:

我已将代码集成到我的样本中。 StartServiceDroid 中的 Start() 开始执行代码。但没有点击 OnStartCommand() 功能。这是我的sample,你能看看吗?我需要在后台或前台模式下每 x 秒 运行 MyFunction()

更新 10-07-2020

@Leon Lu - MSFT 我找到了一个新的解决方案 here。 :)

var second = TimeSpan.FromSeconds(10);

Device.StartTimer(second, () => {
    Debug.WriteLine("Hiiiii");
    return true;
});

我用这段代码创建了一个示例应用程序,它在前台和后台模式下都运行良好。每隔 10 秒,Hiii 消息就会打印在输出框中,即使应用程序在后台 运行ning。

这包含在 Xamarin Forms 中,因此不需要任何特定于平台的逻辑。

这种做法有什么问题吗?

2020 年 7 月 15 日更新

今天在真机上进行了测试。 :)

案例 1:运行 Visual Studio 上的应用程序,服务正在前台模式下调用。将应用程序移至后台(仍然,应用程序在 VS 中 运行ning),当应用程序每 10 秒在后台时,后台服务正在调用。

情况二:正常打开app安装的app,(不是运行在VS中打开),服务在前台调用,把app移到后台,后台服务也不调用10 分钟。

我现在完全糊涂了,当应用程序在 VS 上 运行ning 时后台服务正在调用,而当正常打开已安装的应用程序时不调用。第二种情况,等了10多分钟,添加的服务没有调用。两种情况都是在调试模式下完成的。

这是 xamarin 表单平台的唯一行为吗?如果我们在原生ios平台做,是不是可以每隔x秒触发一次后台服务? Skype 后台服务如何?

测试设备型号:iPhone7 软件版本:13.5.1

在 Android 中,您可以使用 foreground service 让您的 MyFunction() 始终 运行 处于后台。如果您以 xamarin 形式使用它。你可以使用依赖服务来调用前台服务,然后在前台服务中用MyFunction执行你的Timer

下面是使用 DependentService 打开前台服务的简单代码。

  [Service]
    public class DependentService : Service, IService
    {
        public void Start()
        {
            var intent = new Intent(Android.App.Application.Context,
     typeof(DependentService));


            if (Android.OS.Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.O)
            {
                Android.App.Application.Context.StartForegroundService(intent);
            }
            else
            {
                Android.App.Application.Context.StartService(intent);
            }
        }

        public override IBinder OnBind(Intent intent)
        {
            return null;
        }
        public const int SERVICE_RUNNING_NOTIFICATION_ID = 10000;
        public override StartCommandResult OnStartCommand(Intent intent,StartCommandFlags flags, int startId)
        {
            // From shared code or in your PCL

            CreateNotificationChannel();
            string messageBody = "service starting";

            var notification = new Notification.Builder(this, "10111")
            .SetContentTitle("Foreground")
            .SetContentText(messageBody)
            .SetSmallIcon(Resource.Drawable.main)
            .SetOngoing(true)
            .Build();
            StartForeground(SERVICE_RUNNING_NOTIFICATION_ID, notification);

     //=======you can do you always running work here.=====
           var startTimeSpan = TimeSpan.Zero;
           var periodTimeSpan = TimeSpan.FromSeconds(10);

           var timer = new System.Threading.Timer((e) =>
          {
              MyFunction();
          }, null, startTimeSpan, periodTimeSpan);
          



           
            return StartCommandResult.Sticky;
        }


        void CreateNotificationChannel()
        {
            if (Build.VERSION.SdkInt < BuildVersionCodes.O)
            {
                // Notification channels are new in API 26 (and not a part of the
                // support library). There is no need to create a notification
                // channel on older versions of Android.
                return;
            }

            var channelName = Resources.GetString(Resource.String.channel_name);
            var channelDescription = GetString(Resource.String.channel_description);
            var channel = new NotificationChannel("10111", channelName, NotificationImportance.Default)
            {
                Description = channelDescription
            };

            var notificationManager = (NotificationManager)GetSystemService(NotificationService);
            notificationManager.CreateNotificationChannel(channel);
        }
    }

这是我关于如何以 xamarin 形式使用前台服务的演示。

https://github.com/851265601/ForeGroundService

在iOS中,无法实现始终运行您的应用程序在后台运行,因为iOS,只允许正常应用程序(:音频、VoIP、外部配件和蓝牙Newsstand、Location、Fetch(iOS 7+)、远程通知(iOS 7+)应用程序可以一直在后台运行运行,你可以看到this thread)运行 在 10 分钟内在后台运行。可以参考这篇文章

https://docs.microsoft.com/en-us/xamarin/ios/app-fundamentals/backgrounding/ios-backgrounding-techniques/ios-backgrounding-with-tasks