在 OnNavigatedToAsync 中实例化 LocationService 会导致应用挂起

Instantiating LocationService in OnNavigatedToAsync causes app to hang

我目前正在使用 UWPTemplate10 编写位置感知 MVVM 应用程序。作为此应用程序的一部分,我正在显示一个使用 MapControl 的地图页面,这需要显示用户的当前位置。

我正在尝试利用 LocationService,它是 Template10 框架的一部分(尽管该服务当前未包含在 NuGet 包中)。我遇到的问题是,由于此 MSDN article 中提到的约束,我无法调用此服务以获取用户在页面的 OnNavigatedToAsync 方法中的当前位置,即:

Call the RequestAccessAsync before accessing the user’s location. At that time, your app must be in the foreground and RequestAccessAsync must be called from the UI thread. Until the user grants your app permission to their location, your app can't access location data.

这是因为向用户显示了一个对话框,询问是否允许使用设备的位置 - 但由于我们不在 UI 线程上,应用程序只是挂起。

有没有办法在页面加载完成并准备好后调用另一个方法(调用 LocationService)?我已经尝试挂钩来自页面加载事件的命令调用,但同样的问题也出现在那里。

I'm trying to leverage the LocationService which is part of the Template10 framework (although this service is not currently included in the NuGet package).

你说的 LocationService 是什么意思?我找不到任何关于此的文件。我只是在您提供的文档中使用 Geolocator 来测试您的问题,但我无法重现它。

This is because the user is presented with a dialog box asking permission to use the device's location - but as we are not on the UI thread, the app just hangs.

不是,据说RequestAccessAsync方法必须在访问用户位置之前调用,必须从UI线程调用,如果在OnNavigatedToAsync,从UI线程调用,应用不会挂起。

这是我的示例,我创建了一个空的汉堡包模板 10 项目,并在详细信息页面中:

先加4个TextBlock表示在xaml中的状态和位置:

<!--  content  -->
<ScrollViewer Padding="12,8,0,0" RelativePanel.AlignBottomWithPanel="True"
              RelativePanel.AlignLeftWithPanel="True"
              RelativePanel.AlignRightWithPanel="True"
              RelativePanel.Below="pageHeader"
              VerticalScrollBarVisibility="Auto">
    <StackPanel>
        <TextBlock Style="{StaticResource TitleTextBlockStyle}" Text="You passed:" />
        <TextBlock Style="{StaticResource SubtitleTextBlockStyle}" Text="{x:Bind ViewModel.Value, Mode=OneWay, FallbackValue=DesigntimeValue}" />
        <TextBlock Style="{StaticResource SubtitleTextBlockStyle}" Text="{x:Bind ViewModel.Status,Mode=OneWay}" />
        <TextBlock Style="{StaticResource SubtitleTextBlockStyle}" Text="{x:Bind ViewModel.Latitude, Mode=OneWay}" />
        <TextBlock Style="{StaticResource SubtitleTextBlockStyle}" Text="{x:Bind ViewModel.Longitude, Mode=OneWay}" />
        <TextBlock Style="{StaticResource SubtitleTextBlockStyle}" Text="{x:Bind ViewModel.Accuracy, Mode=OneWay}" />
    </StackPanel>
</ScrollViewer>

ViewModel 中的部分代码:

public override async Task OnNavigatedToAsync(object parameter, NavigationMode mode, IDictionary<string, object> suspensionState)
{
    Value = (suspensionState.ContainsKey(nameof(Value))) ? suspensionState[nameof(Value)]?.ToString() : parameter?.ToString();

    StartTracking();

    await Task.CompletedTask;
}

private async void StartTracking()
{
    // Request permission to access location
    var accessStatus = await Geolocator.RequestAccessAsync();

    switch (accessStatus)
    {
        case GeolocationAccessStatus.Allowed:
            _cts = new CancellationTokenSource();
            CancellationToken token = _cts.Token;
            _geolocator = new Geolocator { DesiredAccuracyInMeters = _desireAccuracyInMetersValue };
            Geoposition pos = await _geolocator.GetGeopositionAsync().AsTask(token);
            UpdateLocationData(pos);
            // Subscribe to PositionChanged event to get updated tracking positions
            _geolocator.PositionChanged += OnPositionChanged;

            // Subscribe to StatusChanged event to get updates of location status changes
            _geolocator.StatusChanged += OnStatusChanged;
            break;

        case GeolocationAccessStatus.Denied:
            break;

        case GeolocationAccessStatus.Unspecified:
            break;
    }
}

其他代码可以参考官方Geolocation sample.

Is there a way to call another method (which would call into the LocationService) once the page has completed loading and is ready to go?

我认为问题在于您需要检查导致您的应用程序挂起的确切原因。如果是LocationService的问题,请问post这个服务的文档在评论里吗?

更新:

如果你读过这个LocationService的源代码,你会发现它把Geolocation个API打包成一个服务,如果你使用代码var locationService = new LocationService();var pos = locationService.Position;,那么大部分代码在 StartTracking() 方法中是无用的,他们只是创建另一个 Geolocator,并尝试获取位置,当位置再次更改时获得通知,就像服务所做的那样。 但是RequestAccessAsync还是需要的,而且必须在你new一个LocationService的实例之前调用它,所以你可以这样编码:

public override async Task OnNavigatedToAsync(object parameter, NavigationMode mode, IDictionary<string, object> suspensionState)
{
    Value = (suspensionState.ContainsKey(nameof(Value))) ? suspensionState[nameof(Value)]?.ToString() : parameter?.ToString();
    var accessStatus = await Geolocator.RequestAccessAsync();
    var locationService = new LocationService();
    var pos = locationService.Position;
    //StartTracking();
    await Task.CompletedTask;
}

刚刚测试,效果很好。