-
-
Notifications
You must be signed in to change notification settings - Fork 72
[Android] Foreground Service
A foreground service keeps your app alive while background work is running and is required to show a persistent notification that the user can always see. This is the correct approach for ongoing work such as file uploads, music playback, location tracking, or long-running sync operations.
For one-shot notifications (including scheduled ones) you do not need a foreground service — use
LocalNotificationCenter.Current.Show()instead.
- Your app calls
StartForegroundServiceAsyncwith a description of the work and a notification to display. - The plugin starts an Android
Service, callsstartForeground(), and posts the notification immediately. - Android keeps the service process alive until you call
StopForegroundServiceAsync. - The notification is removed and the service process ends on stop.
Three additions are required in Platforms/Android/AndroidManifest.xml:
<!-- Required for all foreground services -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<!-- Required on Android 14+ for the specific service type you use.
Replace "short_service" with the permission matching your AndroidForegroundServiceType. -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SHORT_SERVICE" />
<!-- Declare the service. Use tools:node="merge" so the value merges with the
plugin's own manifest entry rather than creating a duplicate. -->
<service
android:name="plugin.LocalNotification.NotificationForegroundService"
android:foregroundServiceType="shortService"
android:exported="false"
tools:node="merge" />Make sure the tools namespace is declared on the root <manifest> element:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">AndroidForegroundServiceType |
android:foregroundServiceType |
Extra <uses-permission> (API 34+) |
|---|---|---|
ShortService |
shortService |
FOREGROUND_SERVICE_SHORT_SERVICE |
DataSync |
dataSync |
FOREGROUND_SERVICE_DATA_SYNC |
MediaPlayback |
mediaPlayback |
FOREGROUND_SERVICE_MEDIA_PLAYBACK |
PhoneCall |
phoneCall |
FOREGROUND_SERVICE_PHONE_CALL |
Location |
location |
FOREGROUND_SERVICE_LOCATION |
ConnectedDevice |
connectedDevice |
FOREGROUND_SERVICE_CONNECTED_DEVICE |
Camera |
camera |
FOREGROUND_SERVICE_CAMERA |
Microphone |
microphone |
FOREGROUND_SERVICE_MICROPHONE |
Health |
health |
FOREGROUND_SERVICE_HEALTH |
RemoteMessaging |
remoteMessaging |
FOREGROUND_SERVICE_REMOTE_MESSAGING |
MediaProjection |
mediaProjection |
FOREGROUND_SERVICE_MEDIA_PROJECTION |
SpecialUse |
specialUse |
FOREGROUND_SERVICE_SPECIAL_USE |
Multiple types can be combined (bitwise OR):
android:foregroundServiceType="dataSync|location"ForegroundServiceType = AndroidForegroundServiceType.DataSync | AndroidForegroundServiceType.LocationAccess the Android-specific service by casting LocalNotificationCenter.Current to IAndroidNotificationService:
using Plugin.LocalNotification;
using Plugin.LocalNotification.Core.Models;
using Plugin.LocalNotification.Core.Models.AndroidOption;
#if ANDROID
var androidService = LocalNotificationCenter.Current as IAndroidNotificationService;
if (androidService is not null)
{
await androidService.StartForegroundServiceAsync(new AndroidForegroundServiceRequest
{
ForegroundServiceType = AndroidForegroundServiceType.ShortService,
Notification = new NotificationRequest
{
NotificationId = 1001,
Title = "Syncing data…",
Description = "Your data is being synchronised in the background.",
}
});
}
#endif#if ANDROID
var androidService = LocalNotificationCenter.Current as IAndroidNotificationService;
await androidService?.StopForegroundServiceAsync();
#endifIf you are already on Android and want a cleaner call site, use the AndroidService accessor:
#if ANDROID
await LocalNotificationCenter.AndroidService.StartForegroundServiceAsync(new AndroidForegroundServiceRequest
{
ForegroundServiceType = AndroidForegroundServiceType.DataSync,
Notification = new NotificationRequest
{
NotificationId = 2001,
Title = "Uploading files",
Description = "3 of 10 files uploaded",
Android = { ChannelId = "upload_channel" }
}
});
// … do upload work …
await LocalNotificationCenter.AndroidService.StopForegroundServiceAsync();
#endifThe notification shown by the foreground service uses AndroidOptions.DefaultChannelId unless you specify a different channel. Create a dedicated channel in MauiProgram.cs for the best user experience:
builder.UseLocalNotification(config =>
{
config.AddAndroid(android =>
{
android.AddChannel(new AndroidNotificationChannelRequest
{
Id = "background_work",
Name = "Background Work",
Description = "Shown while background tasks are running",
Importance = AndroidImportance.Low, // Low importance keeps it quiet
EnableVibration = false,
EnableSound = false
});
});
});Then reference the channel in the notification request:
Notification = new NotificationRequest
{
NotificationId = 3001,
Title = "Processing…",
Android = { ChannelId = "background_work" }
}| Value | Bit | Description | Min API |
|---|---|---|---|
None |
0 | No type constraint. | All |
DataSync |
1 | Network upload or download. | 29 |
MediaPlayback |
2 | Audio playback or recording. | 29 |
PhoneCall |
4 | Phone or VoIP call. | 29 |
Location |
8 | Device location tracking. | 29 |
ConnectedDevice |
16 | Connection to a Bluetooth/NFC/USB device. | 29 |
MediaProjection |
32 | Screen capture or media projection. | 29 |
Camera |
64 | Camera stream processing. | 29 |
Microphone |
128 | Microphone access. | 30 |
Health |
256 | Health-related task. | 34 |
RemoteMessaging |
512 | Remote messaging. | 34 |
ShortService |
2048 | Brief user-triggered task (must finish within a few minutes). | 34 |
SpecialUse |
— | Special use approved by Google. | 34 |
On Android 13 (API 33) and earlier, the type value passed to
startForegroundis ignored by the OS. The plugin still passes it for forward compatibility.
| Property | Type | Default | Description |
|---|---|---|---|
ForegroundServiceType |
AndroidForegroundServiceType |
ShortService |
The service type(s) declared in the manifest. |
Notification |
NotificationRequest? |
null |
The notification to show. When null, a minimal notification with the app name as title is generated automatically. |
The service does not provide a direct API to update its notification in place. The typical pattern is to cancel the old notification and re-show a new one using MyNotificationManager.Notify(id, notification) on the same notification identifier:
// Show a plain notification with updated text over the service's persistent notification
await LocalNotificationCenter.Current.Show(new NotificationRequest
{
NotificationId = 1001, // same ID used in StartForegroundServiceAsync
Title = "Syncing data…",
Description = "8 of 10 items done",
Android = { Ongoing = true, AutoCancel = false }
});-
ShortService (API 34+): Android enforces a time limit of a few minutes. Use a different type (e.g.,
DataSync) for long-running operations. -
Background process restrictions: On Android 12+ the system may delay
startForegroundServicecalls when the app is in the background. Start the service from a foreground context (Activity, system broadcast) where possible. -
iOS / Windows / MacCatalyst:
StartForegroundServiceAsyncis only available on Android. Calling it on other platforms is a compile error (access it inside#if ANDROIDor via a cast).