Adding A Foreground Service To Your .NET MAUI Android App – Updated Feb 14, 2023

It’s not well documented and I had to scrape stuff together from several sources and post on a couple of different forums but I have a working foreground service that puts up a notification in the notification tray that is tappable to reopen the app after you close it. Here is the code to enable it by file in your project:

using Android.App;
using Android.Content;
using Android.OS;
using AndroidX.Core.App;
namespace YourMAUIApp;
[Service]
public partial class MyForeGroundService : Service, IService
{
public const string NOTIFICATION_CHANNEL_ID = "10276";
private const int NOTIFICATION_ID = 10923;
private const string NOTIFICATION_CHANNEL_NAME = "notification";
private void StartForegroundService()
{
var intent = new Intent(Android.App.Application.Context, typeof(MainActivity));
var pendingIntentFlags = Build.VERSION.SdkInt >= BuildVersionCodes.S
? PendingIntentFlags.UpdateCurrent |
PendingIntentFlags.Mutable
: PendingIntentFlags.UpdateCurrent;
var pendingIntent = PendingIntent.GetActivity(Android.App.Application.Context, 0, intent, pendingIntentFlags);
var notifcationManager = GetSystemService(NotificationService) as NotificationManager;
if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
{
CreateNotificationChannel(notifcationManager);
}
var notification = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID);
notification.SetContentIntent(pendingIntent);
notification.SetAutoCancel(false);
notification.SetOngoing(true);
notification.SetSmallIcon(Build.VERSION.SdkInt >= BuildVersionCodes.Tiramisu ? Resource.Drawable.ic_notification : Resource.Mipmap.appicon);
notification.SetContentTitle("My Aoo");
notification.SetContentText("My App Service is running");
StartForeground(NOTIFICATION_ID, notification.Build());
}
private static void CreateNotificationChannel(NotificationManager notificationMnaManager)
{
var channel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, NOTIFICATION_CHANNEL_NAME,
NotificationImportance.Low);
notificationMnaManager.CreateNotificationChannel(channel);
}
public override IBinder OnBind(Intent intent)
{
return null;
}
public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
{
StartForegroundService();
return StartCommandResult.Sticky;
}
public void Start()
{
var intent = new Intent(Android.App.Application.Context, typeof(MyForeGroundService));
Android.App.Application.Context.StartForegroundService(intent);
}
public void Stop()
{
var intent = new Intent(Android.App.Application.Context, typeof(MyForeGroundService));
Android.App.Application.Context.StopService(intent);
}
}
namespace YourMAUIApp;
public partial class HomePage : ContentPage
{
public readonly HomeViewModel ViewModel;
public IService Service { get; }
public HomePage(IService _service)
{
InitializeComponent();
ViewModel = new HomeViewModel();
Service = _service;
}
private void Button_OnClicked(object sender, EventArgs e)
{
Service.Start();
}
private void StopButton_OnClicked(object sender, EventArgs e)
{
Service.Stop();
}
}
namespace YourMAUIApp;
public interface IService
{
void Start();
void Stop();
}
view raw IService.cs hosted with ❤ by GitHub
namespace YourMAUIApp;
public static class MauiProgram
{
public static IServiceProvider Services { get; private set; }
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder.Services.AddTransient<IService, MyForeGroundService>();
builder.Services.AddTransient<HomePage>();
builder
.UseMauiApp<App>()
.UseMauiCommunityToolkit()
.UseMauiCommunityToolkitCore()
.UseMauiCommunityToolkitMarkup()
.ConfigureFonts(fonts =>
{
fonts.AddFont("Roboto-Regular.ttf", "RobotoRegular");
})
var app = builder.Build();
Services = app.Services;
return app;
}
}
view raw MauiProgram.cs hosted with ❤ by GitHub

They are out of order for some reason but first, you want to create the interface in the root of your project called IService.cs with two methods Start and Stop. Just copy the example and adjust the namespace. After that is done, in your android folder create the foreground class, name it what you want but use the inherited interface and Service class. Once that is done, edit MauiProgram.cs to add the entries I have in the example for the service, the property, and the two builder services, and then end as I have it where it calls those services and then calls app. On your home page make a property referencing the service and then also a reference to them in the constructor as well to initialize the service.

At this point, you can either call it through a button or have it launch automatically however you want it from that page or whichever page you choose.

Now I am assuming you are only targeting Android in your project, if you are not then your need to create dummy classes in your other project folders that reference the IService interface so that there are no issues and then in whichever page you choose to launch the service from use:

#if ANDROID #ENDIF around the Service.Start()/Service.Stop() to isolate to just Android or if you were smart and blackhole the interface references in your other classes’ implementations then don’t worry about it 🙂

John

Edit!

You need one more thing if you’re targeting Android 13 and up. That would be the Post Notification permission and getting that from the user. Currently .NET MAUI essentials doesn’t have that built in but if you dig into the docs on Microsoft you’ll find out how to create an extension class to add it. Also, in your AndroidManifest.xml you need to add the following:

<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />

Then just do like normal before starting the service, check for the notification permission and request it if it’s not granted.

Edit 2!

If you’re building a Shell App and you see a crash when bringing the app off the back stack then here is the work around!

In your MainActivity.cs add Launchmode = Launchmode.SingleTop where I have it in the example below and it should go away.

using Android.App;
using Android.Content.PM;
namespace YourApp.Platforms.Android;
[Activity(Theme = "@style/Maui.SplashTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize | ConfigChanges.Density, LaunchMode = LaunchMode.SingleTop)]
public class MainActivity : MauiAppCompatActivity
{
}
view raw MainActivity.cs hosted with ❤ by GitHub

Edit 3!

Found this this morning during debugging! In your AndroidManifest.xml in the Application section add the following entry.

android:enableOnBackInvokedCallback=”true

John


Discover more from Spindlecrank.com

Subscribe to get the latest posts to your email.

Leave a Reply