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

时间:2016-08-10 13:28:29

标签: c# xaml mvvm uwp template10

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

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

  

在访问用户的位置之前调用RequestAccessAsync。在   那个时候,你的应用必须在前台和RequestAccessAsync   必须从UI线程调用。在用户授予您的应用之前   对其位置的权限,您的应用无法访问位置数据。

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

有没有办法在页面完成加载后调用另一个方法(可以调用 LocationService )并准备好了?我试过从页面的Loaded事件中调用一个命令调用,但同样的问题也出现了。

1 个答案:

答案 0 :(得分:2)

  

我试图利用作为Template10框架一部分的LocationService(虽然此服务目前不包含在NuGet包中)。

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

  

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

不,据说在访问用户位置之前必须调用RequestAccessAsync方法,并且如果您在RequestAccessAsync中调用此OnNavigatedToAsync方法,则必须从UI线程调用此方法,它是从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的其他代码。

  

有一种方法可以在页面完成加载并准备好之后调用另一个方法(可以调用LocationService)吗?

我认为问题是您需要查看应用程序挂起的确切原因。如果LocationService出现问题,您可以在评论中发布此服务的文档吗?

<强>更新

如果你阅读了这个LocationService的源代码,你可以发现它将Geolocation API打包到一个服务中,如果你使用代码var locationService = new LocationService();var pos = locationService.Position;,那么大多数代码都在StartTracking()方法是无用的,它们只是创建另一个Geolocator,并尝试获取位置,当服务的位置再次更改位置时获取通知。 RequestAccessAsync仍然需要,并且必须在新建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;
}

刚刚测试过,它运行正常。