My app has a service that starts and stops independently of the main activity. The service can be considered to be running all the time. I am looking for the correct way to pass data to this service. There are several ways that I can see:
- Using a BroadcastReceiver on a service side
- Using a LocalBroadcastManager on a service side (currently used)
- Binding to a service
Right now I have some code that works on LocalBroadcastManager. It looks something like this:
Service:
using Android.App;
using Android.Content;
using Android.OS;
using AndroidX.LocalBroadcastManager.Content;
using System.Threading;
using Android.Util;
namespace ExSample {
[Service]
public class LoggingService : Service {
public override IBinder OnBind(Intent intent) {
return null;
}
LocalBroadcastManager localBroadcastManager;
SettingsReceiver receiver;
int sleep_delay;
public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId) {
localBroadcastManager = LocalBroadcastManager.GetInstance(this);
receiver = new SettingsReceiver(this);
localBroadcastManager.RegisterReceiver(receiver, new IntentFilter("SET_SLEEP_DELAY"));
sleep_delay = intent.GetIntExtra("NEW_SLEEP_DELAY", 1000);
new Thread(() => {
while (true) {
Log.Debug("LOOP", "EXECUTE_ME");
Thread.Sleep(sleep_delay);
}
}).Start();
return StartCommandResult.Sticky;
}
public override void OnDestroy() {
localBroadcastManager.UnregisterReceiver(receiver);
base.OnDestroy();
}
class SettingsReceiver : BroadcastReceiver {
LoggingService context;
internal SettingsReceiver(LoggingService context) {
this.context = context;
}
public override void OnReceive(Context ctx, Intent intent) {
context.sleep_delay = intent.GetIntExtra("NEW_SLEEP_DELAY", 1000);
}
}
}
}
Activity:
namespace ExSample {
[Activity(Label = "@string/app_name", Theme = "@style/AppTheme", MainLauncher = true)]
public class MainActivity : AppCompatActivity
{
EditText textbox_delay;
Button log_button;
Intent? service_logging;
LocalBroadcastManager localBroadcastManager;
int sleep_delay = 1000;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.activity_main);
localBroadcastManager = LocalBroadcastManager.GetInstance(this);
log_button = (Button)FindViewById(Resource.Id.button1);
log_button.Click += Log_button_Click;
textbox_delay = (EditText)FindViewById(Resource.Id.editText1);
textbox_delay.TextChanged += Textbox_delay_TextChanged;
}
bool service_enabled = false;
private void Log_button_Click(object sender, EventArgs e) {
if (service_enabled) {
service_enabled = false;
log_button.Text = GetText(Resource.String.main_button_start);
StopService(service_logging);
service_logging = null;
} else {
service_enabled = true;
log_button.Text = GetText(Resource.String.main_button_stop);
service_logging = new Intent(this, typeof(LoggingService));
service_logging.SetAction("SET_SLEEP_DELAY");
service_logging.PutExtra("NEW_SLEEP_DELAY", sleep_delay);
StartService(service_logging);
}
}
private void Textbox_delay_TextChanged(object sender, Android.Text.TextChangedEventArgs e) {
sleep_delay = int.Parse(textbox_delay.Text);
Intent intent = new Intent("SET_SLEEP_DELAY");
intent.PutExtra("NEW_SLEEP_DELAY", sleep_delay);
localBroadcastManager.SendBroadcast(intent);
}
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
{
Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
}
Is the implementation with OnBind more correct in this case? However, there is also a lot of code there. For example, additional flags are needed, implementation of OnUnbind, OnRebind... Would a binding implementation be best solution under my conditions (when the service itself is on a different thread)? Is the cost of binding and unbinding less expensive than sending Intent?
In addition, I do not fully understand how the service will behave during long-term work. The fact is that the OnStartCommand method uses the creation of a new thread, since otherwise the UI hangs. It seems like this thread can be interrupted by the system. Here I do not fully understand the correctness of the flow created in the OnStartCommand method. What's the right thing to do for a long running background service?
A local service (not exported) is assumed that can work even when the application is closed.