Reputation:
Hello friends how are you?. I tell you that I am doing an exercise to run a service in the background. What I want to achieve is that even if the application is closed this service is still running. I have followed the microsoft documentation that says the following: learn.microsoft.com/en-us/xamarin/android/app-fundamentals/services
Isolated Process – An isolated process is a process that runs in its own sandbox, isolated from the rest of the system and with no special permissions of its own. To run a service in an isolated process, the IsolatedProcess property of the ServiceAttribute is set to true as shown in this code snippet:
Sample code:
[Service(Name = "com.xamarin.TimestampService",
IsolatedProcess= true,
Process="com.xamarin.xample.messengerservice.timestampservice_process",
Exported=true)]
I followed all the steps, but the service does not even run.
The code I use is as follows:
MainActivity.cs
public static Intent notificationServiceIntent;
internal bool isStarting = false;
// This is the package name of the APK, set in the Android manifest
const string REMOTE_SERVICE_COMPONENT_NAME = "com.myapp";
// This is the name of the service, according the value of ServiceAttribute.Name
const string REMOTE_SERVICE_PACKAGE_NAME = "com.myapp.notificationservice_process";
protected override void OnCreate(Bundle savedInstanceState)
{
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
base.OnCreate(savedInstanceState);
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
LoadApplication(new App());
CreateNotificationFromIntent(Intent);
notificationServiceIntent = new Intent(this.BaseContext, typeof(PDANotificationService));
ComponentName cn = new ComponentName(REMOTE_SERVICE_PACKAGE_NAME, REMOTE_SERVICE_COMPONENT_NAME);
notificationServiceIntent.SetComponent(cn);
StartService(notificationServiceIntent);
}
NotificationService.cs
[Service(Name = "com.myapp.notificationservice", IsolatedProcess = true,
Process = "com.myapp.notificationservice_process", Exported = true,
Label = "Isolated Process service that has trouble starting")]
public class NotificationService: Service
{
System.Threading.Timer _timer;
INotificationManager notificationManager;
int notificationNumber;
public override IBinder OnBind(Intent intent)
{
return null;
}
public override void OnCreate()
{
base.OnCreate();
}
[return: GeneratedEnum]
public override StartCommandResult OnStartCommand(Intent intent, [GeneratedEnum] StartCommandFlags flags, int startId)
{
if(notificationManager==null)
notificationManager = DependencyService.Get<INotificationManager>();
sendNotification();
base.OnStartCommand(intent, flags, startId);
return StartCommandResult.Sticky;
}
public void sendNotification()
{
Device.StartTimer(new TimeSpan(0, 1, 0), () =>
{
notificationNumber++;
string title = $"Local Notification #{notificationNumber}";
string message = $"You have now received {notificationNumber} notifications!";
notificationManager.ScheduleNotification(title, message);
return true;
});
}
}
BroadcastReceiver.cs
[BroadcastReceiver]
[IntentFilter(new[] { Intent.ActionBootCompleted })]
public class BroadcastReceiver : BroadcastReceiver
{
// This is the package name of the APK, set in the Android manifest
const string REMOTE_SERVICE_COMPONENT_NAME = "com.myapp";
// This is the name of the service, according the value of ServiceAttribute.Name
const string REMOTE_SERVICE_PACKAGE_NAME = "com.myapp.notificationservice_process";
public override void OnReceive(Context context, Intent intent)
{
Log.Info("TestApp", "******* Loading Application *******");
try
{
if (intent.Action.Equals(Intent.ActionBootCompleted))
{
Intent service = new Intent(context, typeof(PDANotificationService));
ComponentName cn = new ComponentName(REMOTE_SERVICE_PACKAGE_NAME, REMOTE_SERVICE_COMPONENT_NAME);
service.SetComponent(cn);
service.AddFlags(ActivityFlags.NewTask);
context.StartService(service);
}
}
catch (Exception ex)
{
Log.Error("TestApp", "******* Error message *******: " + ex.Message);
}
}
}
AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="com.myapp" android:installLocation="auto">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="29" />
<application android:label="My app" android:icon="@mipmap/ic_launcher" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE" />
<uses-permission android:name="android.permission.ACCESS_NOTIFICATION_POLICY" />
<uses-permission android:name="android.permission.MANAGE_DOCUMENTS" />
<uses-permission android:name="android.permission.PERSISTENT_ACTIVITY" />
<uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.REQUEST_COMPANION_RUN_IN_BACKGROUND" />
<uses-permission android:name="android.permission.REQUEST_COMPANION_USE_DATA_IN_BACKGROUND" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.STATUS_BAR" />
I was able to get the service to run when the application is in the background. But now, I need to keep the service running even when the main application is closed.
I've been trying to solve it for almost 2 weeks, but I still can't find the answer. I really, really, would be very grateful if you could help me.
In advance, thank you very much.
Upvotes: 2
Views: 3705
Reputation: 15021
You could use Foreground Sevice by start a foregroud notification to keep alive when your app is closed.
here is a simple sample you could refer to and add into your projects.
1.Create a Service MyService.cs :
[Service(Enabled = true)]
public class MyService : 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, i just want to push a notification every 5 seconds here
runnable = new Action(() =>
{
if (isStarted)
{
DispatchNotificationThatAlarmIsGenerated("I'm running");
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;
}
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);
}
//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.Icon)
.SetVibrate(new long[] { 100, 200, 300, 400, 500, 400, 300, 200, 400 })
.SetSound(null)
.SetChannelId(NOTIFICATION_CHANNEL_ID)
.SetPriority(NotificationCompat.PriorityDefault)
.SetAutoCancel(false)
.SetContentTitle("Mobile")
.SetContentText("My service started")
.SetOngoing(true);
NotificationManagerCompat notificationManager = NotificationManagerCompat.From(this);
StartForeground(NOTIFICATION_SERVICE_ID, builder.Build());
}
//every 5 seconds push a notificaition
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);
Notification.Builder notificationBuilder = new Notification.Builder(this, NOTIFICATION_CHANNEL_ID)
.SetSmallIcon(Resource.Drawable.Icon)
.SetContentTitle("Alarm")
.SetContentText(message)
.SetAutoCancel(true)
.SetContentIntent(pendingIntent);
var notificationManager = (NotificationManager)GetSystemService(NotificationService);
notificationManager.Notify(NOTIFICATION_AlARM_ID, notificationBuilder.Build());
}
}
2.in your activity :
protected override void OnResume()
{
base.OnResume();
StartMyRequestService();
}
public void StartMyRequestService()
{
var serviceToStart = new Intent(this, typeof(MyService));
StartService(serviceToStart);
}
Upvotes: 3