I'm creating a background process in iOS to send location updates to a server, but I'm having trouble controlling it from the main application. Mostly, I'm having trouble killing the service. My app uses a button that start/stops the background process, and also figuring out if the process is still running.
I'm using two methods in a class, a StartListening uses a timer to start location updates, and StopListening that kills the timer. But the I can't always kill the background process, especially if the app has been switched off. I don't have the problem with Android since the API takes care of starting and stopping the location process, but iOS, I have to do this manually.
public void StartListening()
{
//class setup here
if(CLLocationManager.LocationServicesEnabled)
{
locationManager.DesiredAccuracy = 10;
locationManager.StartUpdatingLocation();
taskId = UIApplication.SharedApplication.BeginBackgroundTask(() =>
{
this.timer.Dispose();
});
timer = new Timer(async (o) =>
{
CLLocation location = null;
while(location == null)
{
location = locationManager.Location;
}
if (location != null)
{
//handle location
}
}, null, TimeSpan.Zero, TimeSpan.FromSeconds(UpdateInterval));
UIApplication.SharedApplication.EndBackgroundTask(taskId);
}
}
public void StopListening()
{
this.locationManager.StopUpdatingLocation();
if(taskId != 0)
{
if (this.timer != null)
{
this.timer.Change(Timeout.Infinite, Timeout.Infinite);
this.timer.Dispose();
}
}
}
I'm expecting the StopLocation to stop the background process, and in testing, it works fine, but when I actually try to use the app for a while, it seems it'll "forget" about the background process and not kill it properly. Does anyone have a suggestion on why this is/how to fix this? Any help is appreciated.
Add the following code
if(CLLocationManager.LocationServicesEnabled)
{
locationManager.DesiredAccuracy = 10;
locationManager.RequestWhenInUseAuthorization();
locationManager.AllowsBackgroundLocationUpdates = false;
locationManager.StartUpdatingLocation();
//...
}
RequestWhenInUseAuthorization only works when the foreground operation mode is switched to the background operation mode. For example, if the App switches to the background operation mode, the agent method didUpdateLocations will not continue.
And do not forget add the privacy in info.plist
<key>NSLocationWhenInUseUsageDescription</key>
<string>xxxx</string>
Related
I'm developing UWP app which will assist people with public transportation in my city. It uses geofencing to notify when the device is near some station. All is working fine, but slow. On my lumia 830 it took 4-5 seconds to prepare data for all 250 geofences and create them. Since, geofencemonitor runs under system and can store my geofences even when my app is terminated, I thought it would be smart to not recreate all geofences again and again on every app startup, but create them once and then just reuse them.
But I cant figure out how. When I start my app, Geofencemonitor.Current.Count is 250 and GeofenceMonitor.Current.Status is Ready, but it never fires Geoface state changed event.
My problem:
I m trying to reuse geofences in geofencemonitor. When I run my app and during startup I dont call Geofencemonitor.Current.Geofences.Clear(); and then create and add all my geofences -> geofence state change event is never fired.
Edit: It seems, that geofence status change fires only once after I add geofence to geofencemonitor, independently on if the app was restarted or not. But "used" geofence still remains in geofence monitor. Does geofence have some supended state? Am I missing some geofence property?
Here is how I create geofence:
var geofence = new Geofence(id, geocircle, mask, false, dwellTime);
GeofenceMonitor.Current.Geofences.Add(geofence);
My questions:
"Since, geofencemonitor runs under system and can store my geofences even when my app is terminated" - Is this really true?
If my first questions is true, and it is possible to reuse geofences, what am I doing wrong or do you know some place where I can go deeper into Geofencing in UWP?
Thanks for advice.
Here is how I listen geofences state change event:
ItsValueIs250 = Geofencemonitor.Current.Geofences.Count;
ItsValueIsReady = Geofencemonitor.Current.Status;
public async void OnGeofenceStateChanged(GeofenceMonitor sender, object e)
{
var reports = sender.ReadReports();
await Dispatcher.DispatchAsync( () =>
{
foreach (GeofenceStateChangeReport report in reports)
{
GeofenceState state = report.NewState;
Geofence geofence = report.Geofence;
if (state == GeofenceState.Entered)
{
stationInRange.Add(geofence.Id);
StationInRangeCount = stationInRange.Count().ToString();
}
else if (state == GeofenceState.Exited)
{
stationInRange.Remove(geofence.Id);
}
}
});
}
And maybe this method which I call during startup.
async public Task<string> GeomonitorInitialize()
{
string message = string.Empty;
var accessStatus = await Geolocator.RequestAccessAsync();
switch (accessStatus)
{
case GeolocationAccessStatus.Allowed:
int i = 0;
i = GeofenceMonitor.Current.Geofences.Count;
return message;
case GeolocationAccessStatus.Denied:
message = "GeoDenied";
return message;
case GeolocationAccessStatus.Unspecified:
message = "GeoError";
return message;
default:
message = "Default";
return message;
}
}
I will add any piece of code I use as you will wish. Thank you.
I've written an app for Android using Xamarin Studio. Occasionally, due to other code, the app will hang and become unresponsive. When the user clicks the home button, the audio continues to play instead of stopping. Is there a way for a "hung" app to know that it has been put in the background and force the audio to pause?
protected override void OnDestroy ()
{
DependencyService.Get<IMediaController>().Stop();
// Call base method
base.OnDestroy ();
}
protected override void OnSleep()
{
DependencyService.Get<IMediaController>().Pause();
}
I've determined that the way to do this is to monitor the Android running tasks. This enables me to determine if my app has been backgrounded. I have a thread which runs throughout the apps lifetime, and it is normally killed in OnSleep. If OnSleep is not called, then that thread will determine the app is non-responsive, and it will call OnSleep. This is for Xamarin. I got my ideas from this post. how to check the top activity from android app in background service
//Returns the top running task information
private static ActivityManager activityManager;
private static Android.App.ActivityManager.RunningTaskInfo runningTaskInfo;
public static ComponentName GetTopActivity()
{
if(activityManager == null)
{
activityManager = (ActivityManager) Application.Context.GetSystemService(Context.ActivityService);
}
IList<Android.App.ActivityManager.RunningTaskInfo> runningTasks =
activityManager.GetRunningTasks(1);
if(runningTasks != null && runningTasks.Count > 0)
{
runningTaskInfo = runningTasks[0];
return runningTaskInfo.TopActivity;
}
else
{
return null;
}
}
//This called from my thread every 2 seconds
public bool IsAppVisible()
{
//have to do a complicated version of this to determine the current running tasks //and whether our app is the most prominent.
Android.Content.ComponentName componentName = AndroidUtils.GetTopActivity();
bool isCurrentActivity;
if(componentName != null)
{
isCurrentActivity = string.Compare(componentName.PackageName, "myPackage") == 0;
}
else
{
isCurrentActivity = false;
}
return isCurrentActivity;
}
if(DependencyService.Get<IDeviceUtility>().IsAppVisible() == false)
{
//This needs to be manually called if the app becomes completely unresponive
OnSleep();
Debug.WriteLine("App is no longer visible");
}
We had a problem of some functions that need to be run against an API periodically to get information from a device and the solution I came up with uses a new object to run the thread and the object has some functions to tell the thread to terminate. The object needs to do some setup, run a periodic command and handle shutting down. It also needs to be able to run other commands interleaved with the periodic command. It has three functions that it needs when being setup (Startup, Shutdown and Periodic) and you can pass in a delegate to the command you want interleaved. The startup and periodic command, and the interleaved command, work well enough.
The problem is when trying to stop operation and terminate the thread.
The thread function that executes looks like
private void InterleaverThread()
{
if (this.StartupFunction != null)
{
this.StartupFunction();
}
this.startUpFinished = true;
while (!this.stop)
{
if (this.optCmd != null)
{
this.optCmdResult = this.optCmd();
this.optCmdFinished = true;
}
if (this.stop)
{
break;
}
this.lastPeriodicCmdResult = this.PeriodicFunction();
}
if (this.ShutdownFunction != null)
{
this.ShutdownFunction();
}
this.startUpFinished = false;
}
and the Stop command looks like
public void StopInterleaver()
{
if (!this.IsRunning())
{
return;
}
this.stop = true;
this.interleaverThread.Join();
// SpinWait.SpinUntil(this.IsRunning);
}
When the Thread.Join() command is used the thread never returns but if I used the SpinWait.SpinUntil() the StopInterleaver command returns in the time frame expected. The IsRunning() command just checks the thread IsAlive.
public bool IsRunning()
{
if (this.interleaverThread == null)
{
return false;
}
return this.interleaverThread.IsAlive;
}
The Thread is from System.Threading.
We can't figure out why .Join() doesn't return but SpinWait.WaitUntil does. It seems like they should be doing essentially the same thing.
I would suspect that the compiler is optimizing your loop and not actually checking the stop flag. That is, you have:
while (!this.stop)
{
// do stuff
}
Since the compiler sees that the value of stop can't change inside the function, it can just cache the value in a register.
One way to check if that's a problem is to mark the stop variable volatile, as in:
private volatile bool stop;
That's not a particularly robust way to do it, though. The typical way to handle things is with a CancellationToken. See Cancellation.
For a more detailed look at cancellation and an example, see Polling for Cancellation.
I'm developing a plugin for a 3D modelling application. For this application, there is also a third party plugin (a render engine) that I would like to automate.
What I do is create a list of Camera List<Camera> cameraViews , iterate trough all of them and tell the render engine to start rendering
foreach ( Camera camera in cameraViews )
{
// tell the modellingApplication to apply camera
modellingApplication.ApplyCameraToView(camera);
// tell the render engine to render the image
string path = "somePathWhereIWantToSaveTheImage"
renderEngine.renderCurrentScene(path)
// .renderCurrentScene() seems to be async, because my code, which is on the UI thread
// continues... so:
// make sure that the image is saved before continuing to the next image
while ( !File.Exists(path) )
{
Thread.Sleep(500);
}
}
However, this wont work. The renderingplugin seems to do some async work but, when doing this async work, it is calling the main thread for retrieving information.
I found a workaround for this: Right after calling the render engine to render, call a MessageBox. This will block the code from continuing but async calls are still beïng handled. I know, this is weird behaviour. Whats even weirder is the fact that my MessageBox gets automatically closed when the renderengine has done calling the UI thread for information and continues in his own process. Making my code continue to the while loop to check if the image is saved on the disk.
foreach ( Camera camera in cameraViews )
{
// tell the modellingApplication to apply camera
modellingApplication.ApplyCameraToView(camera);
// tell the render engine to render the image
string path = "somePathWhereIWantToSaveTheImage"
renderEngine.renderCurrentScene(path)
// .renderCurrentScene() seems to be async, because my code, which is on the UI thread
// continues... so:
// show the messagebox, as this will block the code but not the renderengine.. (?)
MessageBox.Show("Currently processed: " + path);
// hmm, messagebox gets automatically closed, that's great, but weird...
// make sure that the image is saved before continuing to the next image
while ( !File.Exists(path) )
{
Thread.Sleep(500);
}
}
This is wonderful, except for the messagebox part. I don't want to show a messagebox, I just want to pause my code without blocking the entire thread (as calls from the renderengine to the ui thread are still accepted)..
It would've been much easier if the renderengine didn't do his work async..
I don't feel this is the best answer, but it hopefully it's what you are looking for. This is how you block a thread from continuing.
// Your UI thread should already have a Dispatcher object. If you do this elsewhere, then you will need your class to inherit DispatcherObject.
private DispatcherFrame ThisFrame;
public void Main()
{
// Pausing the Thread
Pause();
}
public void Pause()
{
ThisFrame = new DispatcherFrame(true);
Dispatcher.PushFrame(ThisFrame);
}
public void UnPause()
{
if (ThisFrame != null && ThisFrame.Continue)
{
ThisFrame.Continue = false;
ThisFrame = null;
}
}
If you want to still receive and do actions on that thread while blocking intermediately, you can do something like this. This feels, um... kinda hacky, so don't just copy and paste without making sure I didn't make some major mistake typing this out. I haven't had my coffee yet.
// Used while a work item is processing. If you have something that you want to wait on this process. Or you could use event handlers or something.
private DispatcherFrame CompleteFrame;
// Controls blocking of the thread.
private DispatcherFrame TaskFrame;
// Set to true to stop the task manager.
private bool Close;
// A collection of tasks you want to queue up on this specific thread.
private List<jTask> TaskCollection;
public void QueueTask(jTask newTask)
{
//Task Queued.
lock (TaskCollection) { TaskCollection.Add(newTask); }
if (TaskFrame != null) { TaskFrame.Continue = false; }
}
// Call this method when you want to start the task manager and let it wait for a task.
private void FireTaskManager()
{
do
{
if (TaskCollection != null)
{
if (TaskCollection.Count > 0 && TaskCollection[0] != null)
{
ProcessWorkItem(TaskCollection[0]);
lock (TaskCollection) { TaskCollection.RemoveAt(0); }
}
else { WaitForTask(); }
}
}
while (!Close);
}
// Call if you are waiting for something to complete.
private void WaitForTask()
{
if (CompleteFrame != null) { CompleteFrame.Continue = false; }
// Waiting For Task.
TaskFrame = new DispatcherFrame(true);
Dispatcher.PushFrame(TaskFrame);
TaskFrame = null;
}
/// <summary>
/// Pumping block will release when all queued tasks are complete.
/// </summary>
private void WaitForComplete()
{
if (TaskCollection.Count > 0)
{
CompleteFrame = new DispatcherFrame(true);
Dispatcher.PushFrame(CompleteFrame);
CompleteFrame = null;
}
}
private void ProcessWorkItem(jTask taskItem)
{
if (taskItem != null) { object obj = taskItem.Go(); }
}
I am trying to detect how long the user has a been inactive within the app or system wide with the Windows 8 Store apps API.
I look into the system trigger User Away however that just tells you when it goes idle. Doesn't let you specify a certain time. I also took a look at Pointer Pressed and try to detect tap or touch events; but this does not work because I am using a webview and unable to capture PointerPressed events through a web view.
Is there any way to detect if a user has been idle for an X amount of time within the app or system wide? Any help is appreciated, thank you!
I ended up detecting key presses and mouse down events with javascript. Inside the LoadCompleted event, I inject javascripts by using AW_WebView.InvokeScript("eval", new string[] { scriptsString });
Key Press Script:
window.document.body.onkeydown = function(event){
window.external.notify('key_pressed');
};
Mouse Down Scripts:
document.onmousedown = function documentMouseDown(e){
window.external.notify('mouse_down');
}
And you can add additional scripts for additional user events.
When a mouse down or key press is detected, window.external.notify("keypress or mouse down") gets executed. This message gets "received" in my WebView_ScriptNotify event. When I receive a message from my WebView, I set a timer. If the timer is already set, it cancels it and starts the timer again. When the timer has finished, some code gets executed.
private void SetTimer(int time)
{
if (!TimerEnabled)
{
return;
}
else
{
if (DelayTimer != null)
{
DelayTimer.Cancel();
DelayTimer = null;
}
//the timeout
TimeSpan delay = TimeSpan.FromSeconds(time);
bool completed = false;
DelayTimer = ThreadPoolTimer.CreateTimer(
(source) =>
{
//
// Update the UI thread by using the UI core dispatcher.
//
Dispatcher.RunAsync(
CoreDispatcherPriority.High,
() =>
{
//
// UI components can be accessed within this scope.
//THIS CODE GETS EXECUTED WHEN TIMER HAS FINISHED
});
completed = true;
},
delay,
(source) =>
{
//
// TODO: Handle work cancellation/completion.
//
//
// Update the UI thread by using the UI core dispatcher.
//
Dispatcher.RunAsync(
CoreDispatcherPriority.High,
() =>
{
//
// UI components can be accessed within this scope.
//
if (completed)
{
// Timer completed.
}
else
{
// Timer cancelled.
}
});
});
}
}
Hope this helps someone! I know this is not a perfect method of getting this done, but for the time being this worked for me.