Hello,
Welcome to our Microsoft Q&A platform!
Starting in Android 8.0 (API level 26), an Android application no longer have the ability to run freely in the background. When an application moves into the background, Android will grant the app a certain amount of time to start and use services. Once that time has elapsed, the app can no longer start any services and any services that were started will be terminated. At this point it is not possible for the app to perform any work.
To keep the service working even when the app is closed, try using a ForegroundService
instead. Here is the sample code:
[Service(Enabled = true)]
public class CustomService : Service
{
private Handler handler;
private Action runnable;
private bool isStarted;
private int DELAY_BETWEEN_LOG_MESSAGES = 5000;
private int NOTIFICATION_SERVICE_ID = 1001;
private int NOTIFICATION_AlARM_ID = 1002;
private string NOTIFICATION_CHANNEL_ID = "1003";
private string NOTIFICATION_CHANNEL_NAME = "MyChannel";
public override void OnCreate()
{
base.OnCreate();
handler = new Handler();
//here is what you want to do always
runnable = new Action(() =>
{
if (isStarted)
{
DispatchNotificationThatAlarmIsGenerated("I'm running");
//the function
handler.PostDelayed(runnable, DELAY_BETWEEN_LOG_MESSAGES);
}
});
}
public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
{
if (isStarted)
{
// service is already started
}
else
{
CreateNotificationChannel();
DispatchNotificationThatServiceIsRunning();
handler.PostDelayed(runnable, DELAY_BETWEEN_LOG_MESSAGES);
isStarted = true;
}
return StartCommandResult.Sticky;
}
private void DispatchNotificationThatServiceIsRunning()
{
var notificationManager = (NotificationManager)GetSystemService(NotificationService);
if (Build.VERSION.SdkInt > BuildVersionCodes.O)
{
var chan = new NotificationChannel(NOTIFICATION_CHANNEL_ID, GetString(Resource.String.abc_action_bar_home_description), NotificationImportance.Default);
chan.LightColor = Android.Graphics.Color.Green;
chan.LockscreenVisibility = NotificationVisibility.Private;
notificationManager.CreateNotificationChannel(chan);
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)
.SetAutoCancel(false)
.SetSmallIcon(Resource.Drawable.abc)
.SetContentTitle("Mobile")
.SetContentText("My service started");
StartForeground(NOTIFICATION_SERVICE_ID, builder.Build());
}
else
{
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)
.SetDefaults((int)NotificationDefaults.All)
.SetSmallIcon(Resource.Drawable.abc)
.SetVibrate(new long[] { 100, 200, 300, 400, 500, 400, 300, 200, 400 })
.SetSound(null)
.SetPriority(NotificationCompat.PriorityDefault)
.SetAutoCancel(false)
.SetContentTitle("Mobile")
.SetContentText("My service started")
.SetOngoing(true);
StartForeground(NOTIFICATION_SERVICE_ID, builder.Build());
}
}
private void DispatchNotificationThatAlarmIsGenerated(string message)
{
var intent = new Intent(this, typeof(MainActivity));
intent.AddFlags(ActivityFlags.ClearTop);
var pendingIntent = PendingIntent.GetActivity(this, 0, intent, PendingIntentFlags.OneShot);
var notificationManager = (NotificationManager)GetSystemService(NotificationService);
if (Build.VERSION.SdkInt > BuildVersionCodes.O)
{
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, "default")
.SetContentTitle("Alarm")
.SetContentText(message)
.SetSmallIcon(Resource.Drawable.abc)
.SetAutoCancel(true);
var chan = new NotificationChannel(NOTIFICATION_CHANNEL_ID, GetString(Resource.String.abc_action_bar_home_description), NotificationImportance.Default);
chan.LightColor = Android.Graphics.Color.Green;
chan.LockscreenVisibility = NotificationVisibility.Private;
notificationManager.CreateNotificationChannel(chan);
notificationManager.Notify(NOTIFICATION_AlARM_ID, notificationBuilder.Build());
}
else
{
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)
.SetDefaults((int)NotificationDefaults.All)
.SetSmallIcon(Resource.Drawable.abc)
.SetVibrate(new long[] { 100, 200, 300, 400, 500, 400, 300, 200, 400 })
.SetSound(null)
.SetPriority(NotificationCompat.PriorityDefault)
.SetAutoCancel(false)
.SetContentTitle("Mobile")
.SetContentText("My service started")
.SetOngoing(true);
notificationManager.Notify(NOTIFICATION_AlARM_ID, notificationBuilder.Build());
}
}
public override void OnTaskRemoved(Intent rootIntent)
{
//base.OnTaskRemoved(rootIntent);
}
public override IBinder OnBind(Intent intent)
{
// Return null because this is a pure started service. A hybrid service would return a binder that would
// allow access to the GetFormattedStamp() method.
return null;
}
public override void OnDestroy()
{
// Stop the handler.
handler.RemoveCallbacks(runnable);
// Remove the notification from the status bar.
var notificationManager = (NotificationManager)GetSystemService(NotificationService);
notificationManager.Cancel(NOTIFICATION_SERVICE_ID);
isStarted = false;
base.OnDestroy();
}
private void CreateNotificationChannel()
{
//Notification Channel
NotificationChannel notificationChannel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, NOTIFICATION_CHANNEL_NAME, NotificationImportance.Max);
notificationChannel.EnableLights(true);
notificationChannel.EnableVibration(true);
notificationChannel.SetVibrationPattern(new long[] { 100, 200, 300, 400, 500, 400, 300, 200, 400 });
NotificationManager notificationManager = (NotificationManager)this.GetSystemService(Context.NotificationService);
notificationManager.CreateNotificationChannel(notificationChannel);
}
}
Check the doc: https://learn.microsoft.com/en-us/xamarin/android/app-fundamentals/services/foreground-services
Best Regards,
Jarvan Zhang
If the response is helpful, please click "Accept Answer" and upvote it.
Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.
Xamarin.Android is just a layer of packaging for the Android native library, without additional operations. I tested the function in a basic Xamarin.Android project, it does work when the app is killed.
Hello JarvanZhang-MSFT,
This I know:
But as a further test I created a native Android project that simply starts a periodic task that writes a timestamp to a db firestore every 15 minutes.
The strange thing is that using this native kotlin project, the workmanager continues to work even when the application is removed from the system recent menu.
If you want to try this too, I am sending you the source of the native mini-app attached from google drive uri, just open it with Android Studio and replace the google-services.json file inside the app folder with a file generated by the console firebase for firestore databases.
https://drive.google.com/file/d/1T0HfNLWpJpiujtHKTVIkCyiInECbUWzy/view?usp=sharing
I tested it on my personal device: Samsung Galaxy A21S (Android 11).
Thank you so much, again.
Did you use the same device to test the Xamarin.Android project and the kotlin project? WorkManager seems only work on all manufacturers. Here is the related link, please check: https://stackoverflow.com/questions/55349488/work-manager-not-working-when-app-is-killed-by-the-user-why
Sign in to comment