Asynchronous Woes - GPS

RobCrossleyRobCrossley USMember ✭✭✭

I'm running into an issue with my application that I was hoping to get some advice on. The high level is that when someone does an action, we want to pull a GPS coordinate. This action results in a new fragment being started, so you start on one fragment, click "Create new" and then the GPS is pulled and you load the next fragment that's essentially an info fragment.

I'm using Xamarin's Geolocator, which is asynchronous. Here's the code in the activity that my fragment is hosted from:

public async Task SingleGPSPull(string Event)
{
    var position = await GetGPSLocation();
    GPSData gps = new GPSData{
        Accuracy = (float) position.Accuracy,
        GPSDateAdded = DateTime.UtcNow,
        Offset = TimeZoneInfo.Local.GetUtcOffset(DateTime.UtcNow).ToString(),
        Latitude = position.Latitude,
        Longitude = position.Longitude,
        ServiceTechNumber = DatabaseManager.GetResourceID().ToString(),
        Notes = Event,
    };

    DatabaseManager.SaveItem<GPSData> (gps);
}

public async Task<Position> GetGPSLocation()
{
    var locator = new Geolocator(this) { DesiredAccuracy = 50 };
    Position position = await locator.GetPositionAsync (timeout: 10000);
    return position;
}

Here's the fragment call to said method:

await fca.SingleGPSPull("Disposition");

Android.Support.V4.App.Fragment fragment = ContactDetailInfoFragment.NewInstance(WorkOrderID, null, assignmentID);
fca.SwitchContent(fragment, true);

fca is just an instance of my Activity. So it pulls a single GPS coordinate and then switches fragments. So this poses 2 problems. 1, Sometimes the task is getting canceled or 2, The app waits (as you'd expect) for the GPS pull and then it switches to the next screen. This is a problem because it seems to the user that the app froze up or is unresponsive, which is undesirable.

So my question is, is there a way I can put Asynchronous calls on background threads so that it happily pulls the GPS in the background without the user ever really knowing it's happening and so that there's no chance the task will be canceled? I imagine this has to happen on the Activity rather than the Fragment as that keeps running and doesn't change as the user navigates screens, so a prime candidate to host said task.

Any help would be much appreciated. Thanks as always!

Posts

  • RobCrossleyRobCrossley USMember ✭✭✭

    Sorry to bump this thread, but I'm still lost on a solution. I tried creating a bound service that starts when you switch fragments and stops once the GPS pull is done, but nothing seems to work. Few major problems I'm running into here:

    public override StartCommandResult OnStartCommand (Intent intent, StartCommandFlags flags, int startId) calls the asynchronous function, but it cannot be asynchronous itself so that's posing a number of problems as it never seems to let the task finish. I know you're not really supposed to call an asynchronous function from a synchronous function, but I don't appear to have any other choice.

    Here's my modified async GPS function:

    public async Task GetGPSLocation()
    {
        try {
            var locator = new Geolocator(this) { DesiredAccuracy = 10000 };
            locator.GetPositionAsync (timeout: 10000).ContinueWith (t => { //Also tried async(t)
            Position position = t.Result;
            GPSData gps = new GPSData{
                Accuracy = (float) position.Accuracy,
                GPSDateAdded = DateTime.UtcNow,
                Offset = TimeZoneInfo.Local.GetUtcOffset(DateTime.UtcNow).ToString(),
                Latitude = position.Latitude,
                Longitude = position.Longitude,
                ServiceTechNumber = DatabaseManager.GetResourceID().ToString(),
                Notes = Event,
            };
    
            DatabaseManager.SaveItem<GPSData> (gps);
        });
    
        } catch (Exception e) {
        }
    }
    

    And here's the many ways I've tried calling it in the synchronous OnStartCommand function:

    //Task Factory
    Task pleaseWork = Task.Factory.StartNew (async () => {
    await GetGPSLocation();
    });
    pleaseWork.Wait(); //also tried Task.WhenAll(pleaseWork);
    
    //Run
    Task pleaseWork = System.Threading.Tasks.Task.Run(() => GetGPSLocation());
    pleaseWork.Wait(); //Tried pleaseWork.Start(), but it didn't like that
    

    I've tried having it return a Position object, and a task of a task. All no luck. Any help on this would be much appreciated - I'm at a bit of a loss.

  • PeterDavisPeterDavis USMember ✭✭✭
    edited June 2014

    You could do something like this:

    Task.Factory.StartNew(()=>
    {
        var locator = new Geolocator(this) { DesiredAccuracy = 10000 };
        Position position = await locator.GetPositionAsync (timeout: 10000);
        GPSData gps = new GPSData{
            Accuracy = (float) position.Accuracy,
            GPSDateAdded = DateTime.UtcNow,
            Offset = TimeZoneInfo.Local.GetUtcOffset(DateTime.UtcNow).ToString(),
            Latitude = position.Latitude,
            Longitude = position.Longitude,
            ServiceTechNumber = DatabaseManager.GetResourceID().ToString(),
            Notes = Event,
        };
    
        DatabaseManager.SaveItem<GPSData> (gps);
        RunOnUIThread(()=> GPSCallback());
    }
    
    private void GPSCallback()
    {
          ... this is the notification that a GPS coordinate was received ...
    }
    

    It basically gets the coordinates and does all the stuff you were doing before and then calls GPSCallback (on the UI thread on the assumption that you want to do something in the UI with that information.)

Sign In or Register to comment.