locked
Why is geolocator.GetGeopositionAsync() taking so long? RRS feed

  • Question

  • I know each task will complete quickly when run individually, they are simple, two are getting web page data, and one is get users position. But when I run my app on an actual device, and even in the emulator, it takes forever to complete, more than a minute maybe several minutes in worst case. So basically my MainPage viewmodel doesn't display any data and I'm waiting and waiting for it to update.

    EDIT: Narrowed down the problem to the  task GeoLocate.OneShotLocationAsync(geoPosition);

    This is what my code looks like

        public partial class MainPage : PhoneApplicationPage
        {
            private ViewModel vm;
            private Position geoPosition = new Position();
            private Position geoPosition2 = new Position();
    
            Uri uri1 = new Uri("http://testing");
            Uri uri2 = new Uri("http://testing");
    
            Task[] tasks = new Task[3];
    
    
    
            // Constructor
            public MainPage()
            {
                InitializeComponent();
              
                vm = new ViewModel();
                DataContext = vm;
    
            }
    
            private async void RunTasks()
            {
                tasks[0] = HttpExtensions.GetMyData("http://testing1");            
                tasks[1] = HttpExtensions.GetMyData("http://testing2");
                tasks[2] = GeoLocate.OneShotLocationAsync(geoPosition);
    
    		//need to wait for all 3 tasks to complete 
    		//before performing subsequent calcuations below
                await Task.WhenAll(tasks);  
    			
                var geoLocation = ((Task<Position>)tasks[2]).Result;
    
                this.geoPosition = geoLocation;
    
                GeoCoordinate2 geoCor2 = new GeoCoordinate2(geoPosition.Lat, geoPosition.Lon, DateTime.Now);
                this.geoPosition2.Lat = geoCor2.Lat2;
                this.geoPosition2.Lon = geoCor2.Lon2;
    
                vm.LAT = geoPosition.Lat;
                vm.LON = geoPosition.Lon;
    
                var CurrVal = ((Task<Stream>)tasks[1]).Result;
    
                double estimate = EstimatedValue.GetCurrentEstimatedValue(CurrVal);
    
    
                //do a whoole bunch of other calculations based on results from tasks above
    			//and display results to viewmodel...
    			//...
            }
    
            protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
            {
                GeoLocate.ConsentPrompt();
                RunTasks();
            }
    
        }






    • Edited by Sal_S Saturday, February 22, 2014 6:26 PM
    Friday, February 21, 2014 2:23 AM

All replies

  • Because one of them either throw an exception or take long to complete.
    Friday, February 21, 2014 6:59 AM
  • Time each one on their own. I'm betting Geo-one-shot is particularly slow.

    http://pauliom.wordpress.com

    Friday, February 21, 2014 7:45 AM
  • Technically, all those tasks can take their sweet time, the httpwebrequests will stall if they don't have a proper internet connection.
    Friday, February 21, 2014 7:49 AM
  • Does it matter if the first and second task are using the same static class and methods?  Would there be any conflict?

    namespace WinPhoneExtensions
    {
    
        public static class HttpExtensions
        {
            public static Task<HttpWebResponse> GetResponseAsync(this HttpWebRequest request)
            {
                var taskComplete = new TaskCompletionSource<HttpWebResponse>();
                request.BeginGetResponse(asyncResponse =>
                {
                    try
                    {
                        HttpWebRequest responseRequest = (HttpWebRequest)asyncResponse.AsyncState;
                        HttpWebResponse someResponse = (HttpWebResponse)responseRequest.EndGetResponse(asyncResponse);
                        taskComplete.TrySetResult(someResponse);
                    }
                    catch (WebException webExc)
                    {
                        HttpWebResponse failedResponse = (HttpWebResponse)webExc.Response;
                        taskComplete.TrySetResult(failedResponse);
                    }
                }, request);
                return taskComplete.Task;
            }
    
    
            public static async Task<Stream> GetMyData(string urlToCall)
            {
                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(urlToCall);
                request.Method = HttpMethod.Get;
                HttpWebResponse response = (HttpWebResponse)await request.GetResponseAsync();
                //using (var sr = new StreamReader(response.GetResponseStream()))
                //{
                    return response.GetResponseStream();
                //}
            }
    
        } 
    
        
    
        public static class HttpMethod
        {
            public static string Head { get { return "HEAD"; } }
            public static string Post { get { return "POST"; } }
            public static string Put { get { return "PUT"; } }
            public static string Get { get { return "GET"; } }
            public static string Delete { get { return "DELETE"; } }
            public static string Trace { get { return "TRACE"; } }
            public static string Options { get { return "OPTIONS"; } }
            public static string Connect { get { return "CONNECT"; } }
            public static string Patch { get { return "PATCH"; } }
        }
    
    }

    Friday, February 21, 2014 2:38 PM
  • No, you *need* to get the timings for each one, so we can draw some conclusions for you

    http://pauliom.wordpress.com

    Friday, February 21, 2014 4:46 PM
  • Hi, How do I do the timings? I tried adding a stopwatch inside the async tasks but it kept causing an exception

    I was doing this

        public static class HttpExtensions
        {
            public static Stopwatch sw;
            public static long http_ticks = 0;
    
            public static Task<HttpWebResponse> GetResponseAsync(this HttpWebRequest request)
            {
                var taskComplete = new TaskCompletionSource<HttpWebResponse>();
                request.BeginGetResponse(asyncResponse =>
                {
                    try
                    {
                        HttpWebRequest responseRequest = (HttpWebRequest)asyncResponse.AsyncState;
                        HttpWebResponse someResponse = (HttpWebResponse)responseRequest.EndGetResponse(asyncResponse);
                        taskComplete.TrySetResult(someResponse);
                    }
                    catch (WebException webExc)
                    {
                        HttpWebResponse failedResponse = (HttpWebResponse)webExc.Response;
                        taskComplete.TrySetResult(failedResponse);
                    }
                }, request);
                return taskComplete.Task;
            }
    
    
            public static async Task<Stream> GetMyData(string urlToCall)
            {
                sw = new Stopwatch();
                sw.Start();
                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(urlToCall);
                request.Method = HttpMethod.Get;
                HttpWebResponse response = (HttpWebResponse)await request.GetResponseAsync();
                //using (var sr = new StreamReader(response.GetResponseStream()))
                //{
                sw.Stop();
                http_ticks = sw.ElapsedTicks;
                    return response.GetResponseStream();
                //}
            }
    
        } 

    But it kept falling to this

            // Code to execute on Unhandled Exceptions
            private void Application_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e)
            {
                if (Debugger.IsAttached)
                {
                    // An unhandled exception has occurred; break into the debugger
                    Debugger.Break();
                }
            }

    Saturday, February 22, 2014 5:38 AM
    1. Start new stopwatch
    2. Await task x
    3. Stop stopwatch

    http://pauliom.wordpress.com

    Saturday, February 22, 2014 8:10 AM
  • You were right it was GeoLocate that was the problem

    Here are my Timings for the HttpRequests

    Task 0 - over cellular
    ElapsedMilliseconds
    1611
    1344
    508
    497
    495
    1762

    Task 0 - over wifi
    ElapsedMilliseconds
    414
    671
    412
    583
    514
    515

    Task 1 - over wifi
    ElapsedMilliseconds
    456
    509
    522
    432
    507
    525

    Task 1 - over cellular
    ElapsedMilliseconds
    1382
    545
    509
    509
    437
    502

    But the GeoLocate Task I could not get a timing because it was taking so long I did not want to wait for it, maybe it would never finish. Same thing happens on the Emulator, plus I tested other apps like Here Maps and I'm able to get my position so I don't think its a problem with the device hardware.

    This is what I tested

                #region test timiing of geolocate
                Position geoPosition = new Position();
                System.Diagnostics.Debug.WriteLine("TIMING GEOLOCATE BEGIN");
                Stopwatch sw = new Stopwatch();
                sw.Start();
                var geoLocation = await GeoLocate.OneShotLocationAsync(geoPosition);
                sw.Stop();
                System.Diagnostics.Debug.WriteLine(sw.ElapsedMilliseconds);
                System.Diagnostics.Debug.WriteLine("TIMING GEOLOCATE END");
                #endregion

    And this I the code for getting the geo position

        /// <summary>
        /// Obtain user location
        /// </summary>
        public class GeoLocate
        {
    
            //Position position = new Position();
    
            public static void ConsentPrompt()
            {
                if (IsolatedStorageSettings.ApplicationSettings.Contains("LocationConsent"))
                {
                    // User has opted in or out of Location
                    return;
                }
                else
                {
                    MessageBoxResult result =
                        MessageBox.Show("This app accesses your phone's location. Is that ok?",
                        "Location",
                        MessageBoxButton.OKCancel);
    
                    if (result == MessageBoxResult.OK)
                    {
                        IsolatedStorageSettings.ApplicationSettings["LocationConsent"] = true;
                    }
                    else
                    {
                        IsolatedStorageSettings.ApplicationSettings["LocationConsent"] = false;
                    }
    
                    IsolatedStorageSettings.ApplicationSettings.Save();
                }
            }
    
            public static async Task<Position> OneShotLocationAsync(Position pos)
            {
    
                //Position pos = new Position();
    
                //if ((bool)IsolatedStorageSettings.ApplicationSettings["LocationConsent"] != true)
                //{
                //    // The user has opted out of Location.
                //    return ;
                //}
    
                Geolocator geolocator = new Geolocator();
                geolocator.DesiredAccuracyInMeters = 50;
    
                try
                {
                    
                    Geoposition geoposition = await geolocator.GetGeopositionAsync(
                        maximumAge: TimeSpan.FromMinutes(5),
                        timeout: TimeSpan.FromSeconds(10)
                        );
    
                    pos.Lat = geoposition.Coordinate.Latitude;
                    pos.Lon = geoposition.Coordinate.Longitude;
    
    
    
                }
                catch (Exception ex)
                {
                    if ((uint)ex.HResult == 0x80004004)
                    {
                        // the application does not have the right capability or the location master switch is off
                        //StatusTextBlock.Text = "location  is disabled in phone settings.";
                    }
                    else
                    {
                        // something else happened acquring the location
                    }
                }
    
                return pos;
            }
        }



    • Edited by Sal_S Saturday, February 22, 2014 6:29 PM
    Saturday, February 22, 2014 5:12 PM
  • The code looks OK on first glance. Something in the cobwebs of my memory was about the order or rather when in the app life cycle you can use this call. Where are you exec it from?

    http://pauliom.wordpress.com

    Saturday, February 22, 2014 11:47 PM
  • I have placed it in a function called RunTasks that gets executed on the MainPage OnNavigatedTo()

    (See original post for how MainPage looks)

            protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
            {
                GeoLocate.ConsentPrompt();
                RunTasks();
            }

    Sunday, February 23, 2014 12:19 AM
  • Update:

    So I did another test.  I commented out all my other code and isolated the task of geo-location to its own function and moved it to App.xaml.cs. 

    I called the function from

            private void Application_Launching(object sender, LaunchingEventArgs e)
            {
                this.runTask();
            }

    using this version of the function results in the deadlock or whatever is delaying the task

            public async void runTask()
            {
                #region test timiing of geolocate
                Position geoPosition = new Position();
                System.Diagnostics.Debug.WriteLine("TIMING GEOLOCATE BEGIN");
                Stopwatch sw = new Stopwatch();
                sw.Start();
                try
                {
                    var geoLocation = await GeoLocate.OneShotLocationAsync(geoPosition);
                }
                catch (System.Exception ex)
                {
                    System.Diagnostics.Debug.WriteLine("ex: " + ex);
                }
                sw.Stop();
                System.Diagnostics.Debug.WriteLine(sw.ElapsedMilliseconds);
                System.Diagnostics.Debug.WriteLine("TIMING GEOLOCATE END");
                #endregion
    
            }


    But then I tried removing asyn await and it worked, it completed the task

            public void runTask()
            {
                #region test timiing of geolocate
                Position geoPosition = new Position();
                System.Diagnostics.Debug.WriteLine("TIMING GEOLOCATE BEGIN");
                Stopwatch sw = new Stopwatch();
                sw.Start();
                try
                {
                    var geoLocation = GeoLocate.OneShotLocationAsync(geoPosition);
                }
                catch (System.Exception ex)
                {
                    System.Diagnostics.Debug.WriteLine("ex: " + ex);
                }
                sw.Stop();
                System.Diagnostics.Debug.WriteLine(sw.ElapsedMilliseconds);
                System.Diagnostics.Debug.WriteLine("TIMING GEOLOCATE END");
                #endregion
    
            }


    TIMING GEOLOCATE BEGIN

    30

    TIMING GEOLOCATE END

    But my next problem is I'm trying to get the tasks into a Task array so that I can wait for all to complete before moving on to subsequent calculations which depend on that data.

    So what do I set my Tass[2] to be?

    This doesn't work

    tasks[2] = GeoLocate.OneShotLocationAsync(geoPosition);
    So do I have to do this instead?

    Geolocator geolocator = new Geolocator();
                geolocator.DesiredAccuracyInMeters = 50;
    
    tasks[2] = geolocator.GetGeopositionAsync(
                        maximumAge: TimeSpan.FromMinutes(5),
                        timeout: TimeSpan.FromSeconds(10)
                        );


    Sunday, February 23, 2014 8:50 PM
  • Try not using the ui thread with task.factory.startnew and see if works

    http://pauliom.wordpress.com

    Sunday, February 23, 2014 10:47 PM
  • Do you mean like this?

                tasks[2] = Task.Run(() =>
                {
                    GeoLocate.OneShotLocationAsync();
                });

    I get this warning, so I'm not sure what to do to fix

    "Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call."

    Monday, February 24, 2014 1:54 AM
  • Actually I got it to work finally

    I found this and changed how get the position

            /// <summary>
            /// To get the current user position in Windows Phone 8 you can use the GeoLocator class that is very comfortable. You get a method containing two overloads.
            /// GetGeopositionAsync() – return the current postion (does not have a timeout)
            /// GetGeopositionAsync(maximumAge, timeout) – returns a cached position
            /// Sometimes GetGeopositionAsync does not return. So you need to add a timeout procedure by your self. This way is works perfect
            /// source:  https://bernhardelbl.wordpress.com/2013/11/26/geolocator-getgeopositionasync-with-correct-timeout/
            /// </summary>
            /// <returns></returns>
            public static async Task<Geoposition> OneShotTest()
            {
                Geolocator geolocator = new Geolocator();
    
                // get the async task
                var asyncResult = geolocator.GetGeopositionAsync();
                var task = asyncResult.AsTask();
    
                // add a race condition - task vs timeout task
                var readyTask = await Task.WhenAny(task, Task.Delay(10000));
                if (readyTask != task) // timeout wins
                    throw new TimeoutException();
    
                // position found within timeout
                var pos = await task;
    
                return pos;
            }

    And then I just use it the same way I did before

    private async void RunTasks()
            {
                tasks[0] = HttpExtensions.GetMyData("http://testing");
    
                tasks[1] = HttpExtensions.GetMyData("http://testing");
    
                tasks[2] = GeoLocate.OneShotTest();
    
    
                await Task.WhenAll(tasks);
    
    
                var geoLocation = ((Task<Geoposition>)tasks[2]).Result;
    
                Position pos = new Position();
                pos.Lat = geoLocation.Coordinate.Latitude;
                pos.Lon = geoLocation.Coordinate.Longitude;
    
                this.geoPosition = pos;
    ///
    ///
    ///
    ///...
    ///...other stuff...
            }
    
    
            protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
            {
                GeoLocate.ConsentPrompt();
                RunTasks();
    
            }


    • Edited by Sal_S Monday, February 24, 2014 2:46 AM
    Monday, February 24, 2014 2:44 AM