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

Posted

技术标签:

【中文标题】Xamarin Forms:即使应用程序在后台,如何每 x 秒运行一次服务?【英文标题】:Xamarin Forms: How to run a service every x seconds even the app is in background? 【发布时间】:2020-10-27 14:24:06 【问题描述】:

我使用以下代码每 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()

2020 年 7 月 10 日更新

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

var second = TimeSpan.FromSeconds(10);

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

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

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

这种方法有什么问题吗?

2020 年 7 月 15 日更新

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

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

案例2:正常打开已安装的app,(VS中没有运行),服务在前台调用,将app移到后台,10分钟后后台服务不调用。

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

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

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

【问题讨论】:

【参考方案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、外部附件和蓝牙报亭、位置、获取(iOS 7+)、远程通知( iOS 7+) 应用程序可以一直在后台运行,您可以在 10 分钟内看到this thread) 在后台运行。你可以参考这篇文章。

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

【讨论】:

你可以设置任何你想要的,我设置的值是这样的线程:github.com/851265601/ForeGroundService/blob/master/…,你可以下载我的demo看完整的代码。 你能分享你的例外吗? Xamarin android 支持 REST api,可以参考这篇博客。 android.jlelse.eu/… @SreejithSrees 抱歉耽搁了,我不在办公室,您提供的解决方案是 5 年前,后台运行任务没有这些限制,但在 Android 8.0 或更高版本和 IOS7.0 之后,有很多限制,我们必须在服务直播的特定平台上实现。 不,IOS不支持后台服务awalys运行。执行您的应用程序是音频、VoIP、外部附件和蓝牙报亭、位置、获取 (iOS 7+)、远程通知 (iOS 7+) 应用程序

以上是关于Xamarin Forms:即使应用程序在后台,如何每 x 秒运行一次服务?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Xamarin.Forms 中创建永无止境的后台服务?

Xamarin Forms 使用 CrossGeolocator 获取前台服务位置更新

在Xamarin.Forms应用程序中离开页面后,Webview不会关闭

Xamarin.Forms - SkiaSharp 在后台线程中绘图

即使在 Xamarin Forms 中滚动页面的最右侧或最下方,如何使轴/索引始终可见?

Xamarin Forms - 每 10 秒获取一次设备位置(当应用程序在前台/后台运行时)