0

I have and application that it will start a foreground service if I enable a switch.

And this foreground service instantiate a class that it is needed to perform the logic of this foreground service.

The instantiation of the class it is performed in the OnCreate() method of the foreground service class, and this could thrown an expcetion if the class can't be instantiate because some data that is needed to perform.

I would like to propagate this exception to the caller of the service, to can handle, but how the exception is thrown in the OnCreate() method, the application exits without the possibility to handle the exception.

This is the code:

Foreground service:

[Service]
public class AlarmasForegroundService : Service
{
    private IServicioAlarmas _servicioAlarmas;



    private void StartForegroundService()
    {
        _notificationManager = (GetSystemService(Context.NotificationService) as NotificationManager)!;

        createNotificationChannel(_notificationManager);

        Notification notification = new Notification.Builder(this, NOTIFICATION_CHANNEL_ID)
            .SetContentTitle("GTS Registros Horarios")
            .SetContentText($"{DateTime.Now}: Se han iniciado las alarmas.")
            .SetSmallIcon(Resource.Drawable.logo32x32)
            .SetAutoCancel(false)
            .SetOngoing(false)
            .Build();


        StartForeground(NOTIFICATION_ID, notification);
    }


    private NotificationChannel? createNotificationChannel(NotificationManager notificationMnaManager)
    {
        if (Build.VERSION.SdkInt < BuildVersionCodes.O) return null;

        NotificationChannel channel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, NOTIFICATION_CHANNEL_NAME, NotificationImportance.Default);
        notificationMnaManager.CreateNotificationChannel(channel);

        return channel;
    }

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


    public override StartCommandResult OnStartCommand(Intent? intent, StartCommandFlags flags, int startId)
    {
        StartForegroundService();

        _servicioAlarmas.Iniciar();

        return StartCommandResult.Sticky;
    }
    #endregion creación del foreground



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

        //this can throw an exception, that i would like to propagete to be handle for the consumer that call the method Iniciar().
        InicializarServicioAlarmas();
    }



    private void InicializarServicioAlarmas()
    {
        _servicioAlarmas = MauiApplication.Current.Services.GetService<IServicioAlarmas>()!;
        IMessenger messenger = MauiApplication.Current.Services.GetService<IMessenger>()!;
        messenger.Register<MensajeTexto>(this, (recipient, message) =>
        {
            ReceptorMensajes(message.Mensaje);
        });
    }



    public void Iniciar()
    {
        var intent = new Intent(Android.App.Application.Context, typeof(AlarmasForegroundService));
        Android.App.Application.Context.StartForegroundService(intent);
    }



    public void Detener()
    {
        _servicioAlarmas.DetenerAsync();


        var intent = new Intent(Android.App.Application.Context, typeof(AlarmasForegroundService));
        Android.App.Application.Context.StopService(intent);
    }
}

This is the consumer that tries to start the foreground service. It is a view model of a view that has a swtich:

partial void OnAlarmasActivasChanged(bool value)
{
    try
    {
        if (value == true)
        {
            _alarmasForegroundService.Iniciar();
        }
        else
        {
            _alarmasForegroundService.Detener();
        }
    }
    catch (Exception ex) 
    {
        //Here handle the exception that it si thrown in the method OnCreate() of the foreground service.
    }
}

Is it possible to catch the exception in the view model to handle it?

Thanks.

Álvaro García
  • 18,114
  • 30
  • 102
  • 193
  • If my answer gets deleted from those guys who never wrote a single Android service in their life - remember, you are looking for inter process communication. Your best choice is implementing a messaging system of sort. BroadcastReceiver, AIDL, Messenger, TCP, search out for more. My experience is on Android, but I am sure that this is the right way for you. – H.A.H. May 29 '23 at 13:26
  • No, it’s not possible for .Net exception to automatically be sent from an Android foreground service to the main app code. You have to write code to send information, using an Android-specific mechanism that can communicate between those two. Search `android communicate between foreground service and main activity`. Any xamarin solution will work same for Maui. – ToolmakerSteve May 29 '23 at 16:06

1 Answers1

0

Follow those steps for Service side:

  1. Move your starting code to OnStartCommand.

  2. Make a try-catch block, put everything in the block.

  3. At the end of the Try block, call StartForeground method. Return Sticky.

  4. In the Catch block call StopSelf(), put the exception data in Extras, send the Intent, return Non-Sticky.

Follow those steps for app side:

  1. Register Broadcast receiver.

  2. Call the start of the service.

This approach uses Intents in Android. You can read what they are here: https://developer.android.com/guide/components/intents-filters

How to use Intents and Broadcast receivers you can check from here: https://learn.microsoft.com/en-us/xamarin/android/app-fundamentals/broadcast-receivers

The end result should be something like:

[BroadcastReceiver(Enabled = true)]
[IntentFilter(new[] { "com.me.testproject.ERROR" })]
public class MySampleBroadcastReceiver : BroadcastReceiver
{
    public override void OnReceive(Context context, Intent intent)
    {
       //The service did not start, the exception is in the Intent extras!!!
    }
}

This is just one way to do it. Other means of IPC in android OS you can see in this thread:

What are the IPC mechanisms available in the Android OS?

H.A.H.
  • 2,104
  • 1
  • 8
  • 21