Search code examples
androidgeolocationandroid-emulatorlocationmaui

.NET MAUI Android Emulator Geolocation.Default.GetLocationAsync() hangs after being called


I have added the requisite entries to the AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application android:allowBackup="true" android:icon="@mipmap/appicon" android:roundIcon="@mipmap/appicon_round" android:supportsRtl="true" android:usesCleartextTraffic="true">
    <meta-data android:name="com.google.android.geo.API_KEY" android:value="12345" />
    <meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version" />
</application>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-feature android:name="android.hardware.location" android:required="false" />
<uses-feature android:name="android.hardware.location.gps" android:required="false" />
<uses-feature android:name="android.hardware.location.network" android:required="false" />
</manifest>

When i run the following:

 try
        {
            _isCheckingLocation = true;

            GeolocationRequest request = new GeolocationRequest(GeolocationAccuracy.Medium, TimeSpan.FromSeconds(10));

            _cancelTokenSource = new CancellationTokenSource();

            Location location = await Geolocation.Default.GetLocationAsync(request, _cancelTokenSource.Token);

            if (location != null)
            {
                Console.WriteLine($"Latitude: {location.Latitude}, Longitude: {location.Longitude}, Altitude: {location.Altitude}");
                return location;
            }
        }
        // Catch one of the following exceptions:
        //   FeatureNotSupportedException
        //   FeatureNotEnabledException
        //   PermissionException
        catch (FeatureNotSupportedException fnsEx)
        {
            // Handle not supported on device exception
            err = fnsEx.Message;
        }
        catch (FeatureNotEnabledException fneEx)
        {
            // Handle not enabled on device exception
            err = fneEx.Message;
        }
        catch (PermissionException pEx)
        {
            // Handle permission exception
            err = pEx.Message;
        }
        catch (Exception ex)
        {
            // Unable to get location
            err = ex.Message;
        }
        finally
        {
            _isCheckingLocation = false;
        }

the emulator asks for permission to access the device location and I click allow and then the debug control never returns. No error is thrown, it just does nothing.


Solution

  • it is in an async method

    That may not be good enough. There must be a complete chain of async/await, starting at the top - the first method that is called. For example, when reached from constructor, how was it called? If there is any step in the chain of calls that lacks await, then the code is NOT running async - it will block the thread it is on. [There should be an intellisense "warning" on the call site that is missing an await.]

    If it is MainThread, then I suspect permission dialog (or perhaps some other UI initialization) caused deadlock. Thus, the hang.

    Async all the way.

    A way to "sidestep" this problem is to start a new async context:

    Dispatcher.Dispatch(async () =>
    {  // Now on MainThread (UI thread).
       ... await ...;
    }
    // DON'T put code here, and expect it to see the result of the code above.
    
    //OR
    
    Task.Run(async () =>
    {   // Now on a background thread.
        ... await ...;
    }
    // DON'T put code here, and expect it to see the result of the code above.
    

    IMPORTANT: Note the comments // DON'T put code here, and expect ....

    This technique is "fire and forget", it queues the async code, but doesn't run it until after the current method returns.