Im using this code to get location updates for my Xamarin Android app. I have 2 questions.
How to make this foreground service to run forever? I've tried to change return StartCommandResult.NotSticky; to return StartCommandResult.Sticky; and remove anything from OnDestroy() method, but OS seems to be unable to recreate service after it was killed or crashed. So, it runs about a half day only, even i've added app to my battery optimization exclude list. How to make it run forever?
How to properly start service from boot?
Here is what i've tried. Added following to MainActivity.cs
[IntentFilter(new[] { Android.Content.Intent.ActionBootCompleted })]
public class BootReceiver : BroadcastReceiver
{
public Context Context { get; set; }
public override void OnReceive(Context context, Intent intent)
{
var stepServiceIntent = new Intent(context, typeof(LocationUpdatesService));
stepServiceIntent.PutExtra("StartedFromBoot", true);
if (Android.OS.Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.O)
{
context.StartForegroundService(stepServiceIntent);
}
else
{
context.StartService(stepServiceIntent);
}
}
}
Edited LocationUpdatesService.cs
public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
{
Log.Info(Tag, "Service started");
var startedFromNotification = intent.GetBooleanExtra(ExtraStartedFromNotification, false);
var startedFromBoot = intent.GetBooleanExtra("StartedFromBoot", false);
if (startedFromBoot)
{
//Preferences.Set("LocationUpdates", true);
StartForeground(NotificationId, StartNotification("",""));
Preferences.Set("foreground", true);
try
{
FusedLocationClient.RequestLocationUpdates(LocationRequest, LocationCallback, Looper.MyLooper());
}
catch (SecurityException unlikely)
{
Preferences.Set("LocationUpdates", false);
Log.Error(Tag, "Lost location permission. Could not request updates. " + unlikely);
}
}
if (startedFromNotification)
{
RemoveLocationUpdates();
StopSelf();
}
return StartCommandResult.Sticky;
}
I got only single location update that way right from boot. After that single update Im geting "Unknown Location" so service doesnt run continuously. So, how to properly start that service from boot to make it run continuously?
Maybe there will be a single solution for both questions, so if there is a way to start fully functional service from boot, then system could recreate it with Sticky flag and run forever.
Actually, the foreground service will keep running when the phone is on. But you can also use the PowerManager.WakeLock to make sure your app always keep alive even the device is sleep.
You can check this case:Xamarin wakelock
In addition, it seems that you want to get the user's location cyclically. So you can run a timed task in the foreground service. There are many ways to do that. Such as:
JobScheduler
AlarmManager
WorkManager
ScheduledThreadPoolExecutor
You can check this case:Xamarin Android - Periodic task execution
Related
I added a functionality to my Xamarin Forms App allowing it to receive Notifications from Azure.
Everything works fine in debug mode, but in release mode, the App crashes when I receive a notification.
I got a logger working with Adb to see the exception. The code I am using is the following:
public void OnPushNotificationReceived(Context context, INotificationMessage message)
{
try
{
var msgData = message.Data;
if (msgData.ContainsKey("EventType"))
{
try
{
if (AllowStoreNotification(msgData["VirtualStoreId"], msgData["EventType"]))
{
SendNotification(message.Title, message.Body);
}
}
catch (KeyNotFoundException ex)
{
new TrackEvent("Could not deliver notification. Parameter error.")
.AddParameter("exception", ex.Message.ToString())
.Send();
}
}
}
catch (Exception ex)
{
Log.Error("Notification", ex.Message);
}
void SendNotification(string title, string body)
{
var notification = new NotificationCompat.Builder(context, "PushNotifications")
.SetContentTitle(title)
.SetContentText(body)
.SetSmallIcon(Resource.Drawable.logo_pink_circle)
.SetAutoCancel(true)
.SetDefaults((int)NotificationDefaults.All)
.SetPriority((int)NotificationPriority.High)
.Build();
if (_notificationManager is null)
{
NotificationManagerCompat.From(context).Notify(0, notification);
}
else
{
_notificationManager.Notify(0, notification);
}
}
}
The _notificationManager is null for version Build.VERSION.SdkInt >= BuildVersionCodes.O.
The exception from de ADB log adb logcat Notification:E *:S:
Notification: no non-static method "Lcom/microsoft/windowsazure/messaging/notificationhubs/BasicNotificationMessage;.getData()Ljava/util/Map;"
Anyone knows how to fix this?
I figured out how to make it work using the skip linking assembly.
The issue is the certificate you uploaded to azure, it's just for development, not for production
You have a few options:
Change the certificate once you move to production
2 clouds, one for development and another for production.
Or just compile your app as production every time you want to test notifications
I'm posting a Unity game on Facebook. I registered and logged into the developer's account (Facebook for developers - hereinafter ffd). But I have a perpetual download, and I do not know how to fix it. And in the background, behind the download, there is a download from Unity, and the game starts (but the download from Facebook overlaps). I compile the game under WEBGL, archive the folder, and attach it to the Web hosting in ffd - all according to the official documentation. The console Blocked opening in a new window because the request was made in a sandboxed frame whose 'allow-popups' permission is not set. I understand the meaning of the error in general, but I do not know how and where to correct it. I do the authentication itself in this way
// Import statements introduce all the necessary classes for this example.
using Facebook.Unity;
using PlayFab;
using PlayFab.ClientModels;
using UnityEngine;
using LoginResult = PlayFab.ClientModels.LoginResult;
public class PlayfabFacebookAuthExample : MonoBehaviour
{
// holds the latest message to be displayed on the screen
private string _message;
public void Start()
{
SetMessage("Initializing Facebook..."); // logs the given message and displays it on the screen using OnGUI method
// This call is required before any other calls to the Facebook API. We pass in the callback to be invoked once initialization is finished
FB.Init(OnFacebookInitialized);
}
private void OnFacebookInitialized()
{
SetMessage("Logging into Facebook...");
// Once Facebook SDK is initialized, if we are logged in, we log out to demonstrate the entire authentication cycle.
if (FB.IsLoggedIn)
FB.LogOut();
// We invoke basic login procedure and pass in the callback to process the result
FB.LogInWithReadPermissions(null, OnFacebookLoggedIn);
}
private void OnFacebookLoggedIn(ILoginResult result)
{
// If result has no errors, it means we have authenticated in Facebook successfully
if (result == null || string.IsNullOrEmpty(result.Error))
{
SetMessage("Facebook Auth Complete! Access Token: " + AccessToken.CurrentAccessToken.TokenString + "\nLogging into PlayFab...");
/*
* We proceed with making a call to PlayFab API. We pass in current Facebook AccessToken and let it create
* and account using CreateAccount flag set to true. We also pass the callback for Success and Failure results
*/
PlayFabClientAPI.LoginWithFacebook(new LoginWithFacebookRequest { CreateAccount = true, AccessToken = AccessToken.CurrentAccessToken.TokenString },
OnPlayfabFacebookAuthComplete, OnPlayfabFacebookAuthFailed);
}
else
{
// If Facebook authentication failed, we stop the cycle with the message
SetMessage("Facebook Auth Failed: " + result.Error + "\n" + result.RawResult, true);
}
}
// When processing both results, we just set the message, explaining what's going on.
private void OnPlayfabFacebookAuthComplete(LoginResult result)
{
SetMessage("PlayFab Facebook Auth Complete. Session ticket: " + result.SessionTicket);
}
private void OnPlayfabFacebookAuthFailed(PlayFabError error)
{
SetMessage("PlayFab Facebook Auth Failed: " + error.GenerateErrorReport(), true);
}
public void SetMessage(string message, bool error = false)
{
_message = message;
if (error)
Debug.LogError(_message);
else
Debug.Log(_message);
}
public void OnGUI()
{
var style = new GUIStyle { fontSize = 40, normal = new GUIStyleState { textColor = Color.white }, alignment = TextAnchor.MiddleCenter, wordWrap = true };
var area = new Rect(0, 0, Screen.width, Screen.height);
GUI.Label(area, _message, style);
}
}
Here the code is sharpened for PlayFab. It is also registered everywhere there. Facebook and Facebook Instant Games are also connected there. I tried other methods of authentication and login in FB, but no matter what I used, this infinite load is always at 0%. PS Facebook SDK is also integrated into Unity through its official package. If you run a compiled project under WEBGL, then everything is fine, a window pops up about logging in to FB (But swears that the site is not c HTTPS (logically, because the launch is from the locale)).enter image description here
My problem is the following . I have created android service which is winning even after the application is closed. When the application is active i can scan for the service and i can see it running and i can stop it. When i close the activity completely and start the app I again scan for active services i cannot find it in the list. So how to stop the service after the restart of the program so i can get indication if service is still running in the background. I use the bellow method for checking the service.
public void IsServiceRunning()
{
ActivityManager activityManager = (ActivityManager)GetSystemService(Context.ActivityService);
var list = activityManager.GetRunningServices(int.MaxValue);
foreach(var item in list)
{
Toast.MakeText(ApplicationContext, item.Service.ClassName + " " + item.Started + " " + item.Service.Class, ToastLength.Short).Show();
Log.Debug("++++++++++++++++++++++++++++++++++ ", item.Service.ClassName + " " + item.Started + " " + item.Service.Class);
if ( item.Service.ClassName.Contains("MyService"))
{
MessagingCenter.Send<string>("true", "CheckRunningService");
//Toast.MakeText(ApplicationContext, item.Service.ShortClassName.ToString(), ToastLength.Short).Show();
return;
}
}
MessagingCenter.Send<string>("false", "CheckRunningService");
}
Service Code
class MyService : Service
{
SmsBroadcastRceiver sms;
public override void OnCreate()
{
base.OnCreate();
}
[return: GeneratedEnum]
public override StartCommandResult OnStartCommand(Intent intent, [GeneratedEnum] StartCommandFlags flags, int startId)
{
//Toast.MakeText(this, "MyService Started", ToastLength.Long).Show();
sms = new SmsBroadcastRceiver();
RegisterReceiver(sms, new IntentFilter("SMS_RECEIVED"));
return StartCommandResult.NotSticky;
}
public override void OnDestroy()
{
UnregisterReceiver(sms);
StopSelf();
//Toast.MakeText(this, "MyService Stopped", ToastLength.Long).Show();
base.OnDestroy();
}
public override IBinder OnBind(Intent intent)
{
return null;
}
}
Try to put android:stopWithTask="true" with service,which is declared in manifest file and
In the service class
Override the onTaskRemoved method and check also you can remove updates of location listener here to stop the location updates
Hope it will help for you
I think i found why once my app is stopped i cannot find it. As stated bellow from the documentation :
getRunningServices(int maxNum)
This method was deprecated in API level 26. As of Build.VERSION_CODES.O, this method is no longer available to third party applications. For backwards compatibility, it will still return the caller's own services.
I have been working on a location-oriented project that I need to be able to track a user's location while the app is terminated.
I have a background service in my Android project and the Geolocator Plugin.
Just for reference, here are my Geolocator settings:
App.xaml.cs
public static async void StartListening()
{
if (CrossGeolocator.Current.IsListening)
return;
CrossGeolocator.Current.DesiredAccuracy = 10;
await CrossGeolocator.Current.StartListeningAsync(TimeSpan.FromSeconds(LOCATION_PING_SECONDS), 1, true, new Plugin.Geolocator.Abstractions.ListenerSettings
{
AllowBackgroundUpdates = true,
PauseLocationUpdatesAutomatically = false
});
CrossGeolocator.Current.PositionChanged += PositionChanged;
CrossGeolocator.Current.PositionError += PositionError;
}
This + my location service for Android work like a charm while the app is running and backgrounded, but obviously everything stops when the app is terminated.
Android/MainActivity.cs
public void StartLocationService()
{
powerManager = (PowerManager) GetSystemService(PowerService);
wakeLock = powerManager.NewWakeLock(WakeLockFlags.Full, "LocationHelper");
// create a new service connection so we can get a binder to the service
locationServiceConnection = new LocationServiceConnection(null);
// this event will fire when the Service connectin in the OnServiceConnected call
locationServiceConnection.ServiceConnected += (object sender, ServiceConnectedEventArgs e) => {
Console.WriteLine("Service Connected");
};
// Starting a service like this is blocking, so we want to do it on a background thread
new Task(() => {
// Start our main service
Console.WriteLine("App", "Calling StartService");
Android.App.Application.Context.StartService(new Intent(Android.App.Application.Context, typeof(LocationService)));
// bind our service (Android goes and finds the running service by type, and puts a reference
// on the binder to that service)
// The Intent tells the OS where to find our Service (the Context) and the Type of Service
// we're looking for (LocationService)
Intent locationServiceIntent = new Intent(Android.App.Application.Context, typeof(LocationService));
Console.WriteLine("App", "Calling service binding");
// Finally, we can bind to the Service using our Intent and the ServiceConnection we
// created in a previous step.
Android.App.Application.Context.BindService(locationServiceIntent, locationServiceConnection, Bind.AutoCreate);
}).Start();
Console.WriteLine("Aquiring Wake Lock");
wakeLock.Acquire();
}
Does anyone know of any tutorials for getting location updates even when the app is terminated? Is this even possible?
Thanks!
Also, I found this Xamarin forum post... The last post says he is able to get updates while the app is terminated from a service, but I have not been able to get the same outcome.
I have written a windows service but when I try to stop the service it says that the service cannot be stopped at this time. Here's my whole class:
public partial class RenewalsService : ServiceBase
{
private readonly ManualResetEvent _shutdownEvent = new ManualResetEvent(false);
private Thread _thread;
public RenewalsService()
{
InitializeComponent();
this.CanStop = true;
}
protected override void OnStart(string[] args)
{
_thread = new Thread(WorkerThread)
{
Name = "Renewals Service Thread",
IsBackground = true
};
_thread.Start();
}
protected override void OnStop()
{
try
{
if (!_shutdownEvent.SafeWaitHandle.IsClosed)
{
_shutdownEvent.Set();
}
if (_thread.IsAlive)
{
if (!_thread.Join(3000))
{
// give the thread 3 seconds to stop
_thread.Abort();
}
}
}
catch (Exception ex)
{
// _thread.Join may raise an error at this point. If it does we dont care. We dont care about any other exceptions
// since we are already in the process of closing the service.
}
finally
{
IError logger = new Logger();
Exception ex = new Exception("The Renewals service has been stopped.");
logger.Log(this, SeverityEnum.Warning, ex);
Environment.ExitCode = 0;
Environment.Exit(Environment.ExitCode);
}
}
private void WorkerThread()
{
try
{
while (!_shutdownEvent.WaitOne(1))
{
string timeToRun = ConfigurationManager.AppSettings["RunTime"];
string[] timeStrings = timeToRun.Split(':');
TimeSpan runtime = new TimeSpan(0, Int32.Parse(timeStrings[0]), Int32.Parse(timeStrings[1]), Int32.Parse(timeStrings[2]));
if (DateTime.Today.TimeOfDay.Hours == runtime.Hours &&
DateTime.Today.TimeOfDay.Minutes == runtime.Minutes)
{
Renewals renewals = new Renewals();
renewals.GenerateRenewal();
}
}
}
catch (Exception ex)
{
IError logger = new Logger();
logger.Log(this, SeverityEnum.Warning, ex);
this.OnStop();
}
}
}
What's missing to ensure the user can stop the service.
Your code looks ok to me, so here's a couple of things to check.
First, does the GenerateRenewal() method take a long time to complete? If so, you might need to periodically check _shutdownEvent inside that method for a timely shutdown. Of course, you've marked the thread as a background thread so it should shut down when you tell the service to stop anyway. I haven't seen background threads hold up process termination, but I guess there's always that chance.
Second, the more likely culprit to me is that the service has already shut down due to an exception. The Services console doesn't automatically refresh when a service shuts down, so it's possible you see the Stop link available to you when it shouldn't be. If you hit F5, the console will refresh, and if your service has stopped, the Start link should be the only one available. Check your log files to see if your exception handlers have been triggered.
UPDATE
So it looks like your WorkerThread() method is throwing an exception, which causes the service to stop. This explains why the Stop link is giving you the error message when you click it.
Providing you have sufficient permissions on your box, use this link to debug your service to find out why the exception is occurring.
HTH
The base ServiceBase class calls your overridden virtual method OnStop() when the Windows Service Control Manager ("the SCM") has sent the service a "Stop" command. In the method's implementation you are supposed to do whatever is necessary to get your service to a stopped state, then return from the method back to the ServiceBase class, which handles the interaction with the SCM, in this case to tell the SCM that your service is now stopped. The SCM will decide when your service process should be terminated, and the ServiceBase class handles that without you needing to do anything explicit.
For a well-behaved service, you should either just return at the end of your OnStop method, or throw an exception. The ServiceBase class will handle things appropriately, including logging your exception, if you have thrown one, as an error in the Windows Event Log. If your method may take a while to get your service stopped, you should call base.RequestAdditionalTime() at the appropriate points, so the base class can tell the SCM that you haven't just hung, your service is in the process of stopping.
I think your main problem lies in these lines:
Environment.ExitCode = 0;
Environment.Exit(Environment.ExitCode);
You never return to the base class at all... so the ServiceBase class never has a chance to respond gracefully to the SCM... you are just unilaterally terminating the process hosting your service. This is not what a well-behaved Windows service does.
The ServiceBase class is designed to be able to support multiple services hosted in a single service process. Individual services should not concern themselves with the lifetime of the host service process, only with the logical state of their own service.