Home > OS >  Xamarin forms, foreground service does not work when the mobile phone is locked
Xamarin forms, foreground service does not work when the mobile phone is locked

Time:03-18

I'm creating an application that sends user position information to the server every 10 minutes. For the purpose of functionality even when the application is in the background or the mobile is locked, I used the Foreground service. When the application is built directly from Visual Studio to the emulator or via USB to a real device, everything works even when the device is locked. The moment the device disconnects or a release build is performed - the Foreground service does not send information when the device is locked. Does anyone have experience with this? Thank you

Shared code:

MainPage.xaml.cs:

if (MainViewModel.WorkingActions.Contains(buttonText))
{
     DependencyService.Get<IBackgroundService>().StartService(1);

     MessagingCenter.Subscribe<Application>(this, "Ping", async (sender) =>
     {
         await PingWorkingToServer();
     });
 }
 else
 {
     DependencyService.Get<IBackgroundService>().StopService();
 }

IBackgroundService.cs

public interface IBackgroundService
{
    // 1 = logged in, 2 = not logged in
    void StartService(int pingType);
    void StopService();
}

Android:

AndroidServiceHelper.cs

internal class AndroidServiceHelper : IBackgroundService
{
    private static readonly Context context = global::Android.App.Application.Context;
    private static readonly Intent intent = new Intent(context, typeof(DependentService));
    private bool isRunning = false;
    // 1 = logged in 2 = not logged in
    public void StartService(int pingType)
    {
        intent.PutExtra("PingType", pingType);
        if(!isRunning)
        {
            if (Android.OS.Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.O)
            {
                context.StartForegroundService(intent);
            }
            else
            {
                context.StartService(intent);
            }
            isRunning = true;
        }
    }

    public void StopService()
    {
        context.StopService(intent);
        isRunning = false;
    }
}

DependentService.cs

[Service(Enabled = true)]
public class DependentService : Service
{
    private Handler handler;
    private Action runnable;
    private bool isStarted;
    private int DELAY_BETWEEN_MESSAGES;
    private readonly int NOTIFICATION_SERVICE_ID = 1001;
    private readonly int NOTIFICATION_PING_ID = 1002;
    private readonly string NOTIFICATION_CHANNEL_ID = "1003";
    private readonly string NOTIFICATION_CHANNEL_NAME = "MyChannel";
    private const int ONE_MINUTE_MILIS = 60000;
    private int PingType = -1;

    public override void OnCreate()
    {
        base.OnCreate();

        var minutes = Xamarin.Essentials.Preferences.Get("PingTime", 10);

        DELAY_BETWEEN_MESSAGES = minutes * ONE_MINUTE_MILIS;

        handler = new Handler();

        runnable = new Action(() =>
        {
            if (isStarted)
            {
                DispatchNotificationThatPingIsGenerated();
                handler.PostDelayed(runnable, DELAY_BETWEEN_MESSAGES);
            }
        });
    }

    private void DispatchNotificationThatPingIsGenerated()
    {
        var intent = new Intent(this, typeof(MainActivity));
        intent.AddFlags(ActivityFlags.ClearTop);

        Notification.Builder notificationBuilder = new Notification.Builder(this, NOTIFICATION_CHANNEL_ID)
            .SetSmallIcon(Resource.Drawable.dochazka_icon)
            .SetContentTitle("Ukládání pozice")
            .SetContentText("Právě odesílám vaši polohu.")
            .SetAutoCancel(true);

        var notificationManager = (NotificationManager)GetSystemService(NotificationService);
        notificationManager.Notify(NOTIFICATION_PING_ID, notificationBuilder.Build());

        if (PingType == 1)
        {
            MessagingCenter.Send(Xamarin.Forms.Application.Current, "Ping");
        }
        else if (PingType == 2)
        {
            MessagingCenter.Send(Xamarin.Forms.Application.Current, "PingLocally");
        }
    }

    public override IBinder OnBind(Intent intent)
    {
        return null;
    }

    public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
    {
        if (isStarted)
        {
            // service is already started
        }
        else
        {
            PingType = intent.GetIntExtra("PingType", -1);
            CreateNotificationChannel();
            DispatchNotificationThatServiceIsRunning();

            handler.PostDelayed(runnable, DELAY_BETWEEN_MESSAGES);
            isStarted = true;
        }
        return StartCommandResult.Sticky;
    }

    //start a foreground notification to keep alive 
    private void DispatchNotificationThatServiceIsRunning()
    {
        NotificationCompat.Builder builder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)
               .SetDefaults((int)NotificationDefaults.All)
               .SetSmallIcon(Resource.Drawable.dochazka_icon)
               .SetSound(null)
               .SetChannelId(NOTIFICATION_CHANNEL_ID)
               .SetPriority(NotificationCompat.PriorityDefault)
               .SetAutoCancel(false)
               .SetContentTitle("Ukládání pozice")
               .SetContentText("Jste v práci, aplikace pravidelně ukládá polohu.")
               .SetOngoing(true);

        StartForeground(NOTIFICATION_SERVICE_ID, builder.Build());
    }

    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);

        NotificationManager notificationManager = (NotificationManager)GetSystemService(NotificationService);
        notificationManager.CreateNotificationChannel(notificationChannel);
    }
}

AndroidManifest.xml

<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="30" />
<application android:label="Docházka lokalita" android:theme="@style/MainTheme" android:icon="@mipmap/launcher_foreground">
    <service android:name=".DependentService" android:foregroundServiceType="location" />
</application>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-feature android:name="android.hardware.location" android:required="false" />
<uses-feature android:name="android.hardware.location.gps" android:required="false" />
<uses-feature android:name="android.hardware.location.network" android:required="false" />
<queries>
    <intent>
        <action android:name="android.support.customtabs.action.CustomTabsService" />
    </intent>
</queries>

CodePudding user response:

You can use wakeloke to keep the cpu wakeup state after the screen is off or locked so that the service continues to run. like:

public override void OnDestroy()
{
    if (wakeLock != null)
    {
        wakeLock.Release();
        wakeLock = null;
    }
    ...
    base.OnDestroy();
}
  • Related