如何在 .NET Maui 中创建后台服务

Net*_*ity 35 android maui

我是移动应用程序开发新手,正在学习 .NET Maui。我正在创建的应用程序需要侦听加速计事件,并在事件满足特定条件时向 Web 服务发送通知。我正在努力解决的问题是如何让应用程序在后台运行,即没有可见的 UI,而不进入睡眠状态,因为我希望用户完全关闭 UI。所以我认为应用程序需要作为某种服务运行,并可以选择在需要时显示 UI - 如何才能做到这一点?

Lea*_*oza 31

我知道已经有一段时间了,但会为未来的用户发布答案。

首先我们需要了解后台服务取决于我们使用的平台。(感谢 Jason)我将专注于 ANDROID,基于Xamarin 文档(感谢 Eli),适应毛伊岛。

由于我们正在与ANDROID合作,因此在MauiProgram上我们将添加以下内容:

   /// Add dependecy injection to main page
   builder.Services.AddSingleton<MainPage>();
 
#if ANDROID
   builder.Services.AddTransient<IServiceTest, DemoServices>();
#endif
Run Code Online (Sandbox Code Playgroud)

我们创建了 DI 接口,它为我们提供了启动和停止前台服务的方法

public interface IServiceTest
{
    void Start();
    void Stop();
}
Run Code Online (Sandbox Code Playgroud)

然后,在平台代码之前,我们需要在AndroidManifest.xml上添加 Android 权限:

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
Run Code Online (Sandbox Code Playgroud)

Android 主要活动

public class MainActivity : MauiAppCompatActivity
{
   //set an activity on main application to get the reference on the service
   public static MainActivity ActivityCurrent { get; set; }
   public MainActivity()
   {
      ActivityCurrent = this;
   }
}
Run Code Online (Sandbox Code Playgroud)

最后我们创建 Android 前台服务。检查下面的评论。另外,在 xamarin 文档上,它们显示了通知生成器的不同属性。

[Service]
public class DemoServices : Service, IServiceTest //we implement our service (IServiceTest) and use Android Native Service Class
{
   public override IBinder OnBind(Intent intent)
   {
      throw new NotImplementedException();
   }
   
   [return: GeneratedEnum]//we catch the actions intents to know the state of the foreground service
   public override StartCommandResult OnStartCommand(Intent intent, [GeneratedEnum] StartCommandFlags flags, int startId)
   {
      if (intent.Action == "START_SERVICE")
      {
         RegisterNotification();//Proceed to notify
      }
      else if (intent.Action == "STOP_SERVICE")
      {
         StopForeground(true);//Stop the service
         StopSelfResult(startId);
      }
      
      return StartCommandResult.NotSticky;
   }
    
   //Start and Stop Intents, set the actions for the MainActivity to get the state of the foreground service
   //Setting one action to start and one action to stop the foreground service
   public void Start()
   {
      Intent startService = new Intent(MainActivity.ActivityCurrent, typeof(DemoServices));
      startService.SetAction("START_SERVICE");
      MainActivity.ActivityCurrent.StartService(startService);
   }
    
   public void Stop()
   {
      Intent stopIntent = new Intent(MainActivity.ActivityCurrent, this.Class);
      stopIntent.SetAction("STOP_SERVICE");
      MainActivity.ActivityCurrent.StartService(stopIntent);
   }
    
   private void RegisterNotification()
   {
      NotificationChannel channel = new NotificationChannel("ServiceChannel", "ServiceDemo", NotificationImportance.Max);
      NotificationManager manager = (NotificationManager)MainActivity.ActivityCurrent.GetSystemService(Context.NotificationService);
      manager.CreateNotificationChannel(channel);
      Notification notification = new Notification.Builder(this, "ServiceChannel")
         .SetContentTitle("Service Working")
         .SetSmallIcon(Resource.Drawable.abc_ab_share_pack_mtrl_alpha)
         .SetOngoing(true)
         .Build();
    
      StartForeground(100, notification);
   }
}
Run Code Online (Sandbox Code Playgroud)

现在我们的前台服务在 Android 上运行,显示一条通知(“服务正在运行”)。每次开始。我制作了一个显示消息前台服务,以便在测试时更好地查看它,在您的情况下,如果您想要的话,它应该关闭应用程序,但功能是相同的。

因此,让我们的后台服务工作只留下一种调用它的方法,因此在我们的主页(作为示例)上我将执行以下操作:

主页.xaml

<VerticalStackLayout>
   <Label
      Text="Welcome to .NET Multi-platform App UI"
      FontSize="18"
      HorizontalOptions="Center" />
    
   <Button
      x:Name="CounterBtn"
      Text="start Services"
      Clicked="OnServiceStartClicked"
      HorizontalOptions="Center" />
    
   <Button Text="Stop Service" Clicked="Button_Clicked"></Button>
    
</VerticalStackLayout>
Run Code Online (Sandbox Code Playgroud)

MainPage.xaml.cs

public partial class MainPage : ContentPage
{
   IServiceTest Services;

   public MainPage(IServiceTest Services_)
   {
      InitializeComponent();
      ToggleAccelerometer();
      Services = Services_;
   }

   //method to start manually foreground service
   private void OnServiceStartClicked(object sender, EventArgs e)
   {
      Services.Start();
   }

   //method to stop manually foreground service
   private void Button_Clicked(object sender, EventArgs e)
   {
      Services.Stop();
   }
   
   //method to work with accelerometer
   public void ToggleAccelerometer()
   {
      if (Accelerometer.Default.IsSupported)
      {
         if (!Accelerometer.Default.IsMonitoring)
         {
            Accelerometer.Default.ReadingChanged += Accelerometer_ReadingChanged;
            Accelerometer.Default.Start(SensorSpeed.UI);
         }
         else
         {
            Accelerometer.Default.Stop();
            Accelerometer.Default.ReadingChanged -= Accelerometer_ReadingChanged;
         }
      }
   }
   
   //on accelerometer property change we call our service and it would send a message
   private void Accelerometer_ReadingChanged(object sender, AccelerometerChangedEventArgs e)
   {
      Services.Start(); //this will never stop until we made some logic here
   }
}
Run Code Online (Sandbox Code Playgroud)

这是一个很长的答案,如果有更多关于此的官方文档就太好了!希望能帮助到你!如果有人可以提供有关 IOS、Windows、MacCatalyst 的更多信息,那就太棒了!

  • 非常好的总结!IServiceTest 如何注入到 MainPage 构造函数中? (2认同)

小智 8

我的代表太低,无法添加评论

要添加 Leandro 的答案,您必须在正确的平台操作系统中指定使用情况。否则,您将无法使用智能感知来添加用途。

从此图中所示的下拉列表中单击 Android 操作系统: Android 操作系统下拉选择

现在您可以使用智能感知来添加用途。

using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
Run Code Online (Sandbox Code Playgroud)