英文:
GetLocationAsync() Never Returns - Android Mobile App
问题
I'm trying to rectify a bug in a mobile app. When calling the below, it never returns and hangs the app.
All location permissions have been requested in MainApplication.cs under the Android platform, using:
使用以下代码在Android平台的MainApplication.cs中请求了所有位置权限:
using Android.App;
using Android.Runtime;
[assembly: UsesPermission(Android.Manifest.Permission.AccessCoarseLocation)]
[assembly: UsesPermission(Android.Manifest.Permission.AccessFineLocation)]
[assembly: UsesFeature("android.hardware.location", Required = false)]
[assembly: UsesFeature("android.hardware.location.gps", Required = false)]
[assembly: UsesFeature("android.hardware.location.network", Required = false)]
They are also specified in the manifest file (not sure if this is an issue).
这些权限也在清单文件中指定(不确定是否是问题)。
I'm testing with a button event:
我正在使用按钮事件进行测试:
private async void OnCounterClicked(object sender, EventArgs e)
{
GetCachedLocation().GetAwaiter().GetResult();
await DisplayAlert("Location Data", $"{_lat} : {_lon}", "OK");
}
Which fires the following:
这会触发以下事件:
public async Task<bool> GetCachedLocation()
{
try
{
Location location = await Geolocation.Default.GetLastKnownLocationAsync();
if (location != null)
{
_lat = location.Latitude;
_lon = location.Longitude;
return true;
}
else
{
await GetCurrentLocation();
}
}
catch (FeatureNotSupportedException fnsEx) {
Console.WriteLine(fnsEx.Message);
}
catch (FeatureNotEnabledException fneEx)
{
Console.WriteLine(fneEx.Message);
}
catch (PermissionException pEx)
{
Console.WriteLine(pEx.Message);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
return true;
}
If there is no existing Location cached, it runs this:
如果没有现有的位置缓存,它将运行以下代码:
public async Task<bool> GetCurrentLocation()
{
try
{
_isCheckingLocation = true;
GeolocationRequest request = new(GeolocationAccuracy.Medium, TimeSpan.FromSeconds(5));
_cancelTokenSource = new CancellationTokenSource();
Location location = await Geolocation.Default.GetLocationAsync(request, _cancelTokenSource.Token);
if (location != null)
{
_lat = location.Latitude;
_lon = location.Longitude;
return true;
}
}
catch (Exception ex)
{
// Unable to get location
Console.WriteLine(ex.ToString());
return false;
}
finally
{
_isCheckingLocation = false;
}
}
It never gets out of the call to GetLocationAsync(), just hangs. I've tried putting WriteLines above and below the call, and only the one preceding it prints, so it does never come back out.
它永远无法从调用GetLocationAsync()中返回,一直挂起。我已尝试在调用上下方添加WriteLines,只有前面的打印出来,所以它永远不会返回。
I've seen a fair few posts about this sort of thing, although none of those solutions have worked in my instance. I've tried playing around with the request timeout interval, to no avail. I've also tried using the overload taking zero params, which also hangs.
我看过一些关于这种情况的帖子,但没有一个解决方案适用于我的情况。我尝试了调整请求超时间隔,但没有成功。我还尝试使用不带参数的重载,但也会挂起。
I feel I may be missing something obvious. Any suggestions would be welcome!
我觉得我可能遗漏了一些明显的东西。欢迎任何建议!
英文:
I'm trying to rectify a bug in a mobile app. When calling the below, it never returns and hangs the app.
All location permissions have been requested in MainApplication.cs under the Android platform, using:
using Android.App;
using Android.Runtime;
[assembly: UsesPermission(Android.Manifest.Permission.AccessCoarseLocation)]
[assembly: UsesPermission(Android.Manifest.Permission.AccessFineLocation)]
[assembly: UsesFeature("android.hardware.location", Required = false)]
[assembly: UsesFeature("android.hardware.location.gps", Required = false)]
[assembly: UsesFeature("android.hardware.location.network", Required = false)]
They are also specified in the manifest file (not sure if this is an issue).
I'm testing with a button event:
private async void OnCounterClicked(object sender, EventArgs e)
{
GetCachedLocation().GetAwaiter().GetResult();
await DisplayAlert("Location Data", $"{_lat} : {_lon}", "OK");
}
Which fires the following:
public async Task<bool> GetCachedLocation()
{
try
{
Location location = await Geolocation.Default.GetLastKnownLocationAsync();
if (location != null)
{
_lat = location.Latitude;
_lon = location.Longitude;
return true;
}
else
{
await GetCurrentLocation();
}
}
catch (FeatureNotSupportedException fnsEx) {
Console.WriteLine(fnsEx.Message);
}
catch (FeatureNotEnabledException fneEx)
{
Console.WriteLine(fneEx.Message);
}
catch (PermissionException pEx)
{
Console.WriteLine(pEx.Message);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
return true;
}
If there is no existing Location cached, it runs this:
public async Task<bool> GetCurrentLocation()
{
try
{
_isCheckingLocation = true;
GeolocationRequest request = new(GeolocationAccuracy.Medium, TimeSpan.FromSeconds(5));
_cancelTokenSource = new CancellationTokenSource();
Location location = await Geolocation.Default.GetLocationAsync(request, _cancelTokenSource.Token);
if (location != null)
_lat = location.Latitude;
_lon = location.Longitude;
return true;
}
catch (Exception ex)
{
// Unable to get location
Console.WriteLine(ex.ToString());
return false;
}
finally
{
_isCheckingLocation = false;
}
}
It never gets out of the call to GetLocationAsync(), just hangs. I've tried putting WriteLines above and below the call, and only the one preceding it prints, so it does never come back out.
I've seen a fair few posts about this sort of thing, although none of those solutions have worked in my instance. I've tried playing around the with request timeout interval, to no avail. I've also tried using the overload taking zero params, which also hangs.
I feel I may be missing something obvious. Any suggestions would be welcome!
答案1
得分: 3
这是问题:
GetCachedLocation().GetAwaiter().GetResult();
在这种情况下,在一个单线程同步上下文中(例如UI线程),这种阻塞异步代码的方式会引发死锁,我在我的博客中有描述。
要解决这个问题,使用 await
:
await GetCachedLocation();
await
是消费异步代码的自然方式。跳过 await
,丢弃返回的任务并阻塞返回的任务都是不常见的情况。这个指导原则通常被称为“async
all the way”。
英文:
Here's the problem:
GetCachedLocation().GetAwaiter().GetResult();
That kind of blocking on asynchronous code (while within a one-thread-at-a-time synchronization context, in this case, a UI thread) will cause deadlocks, as I describe on my blog.
To solve it, use await
instad:
await GetCachedLocation();
await
is the natural way to consume asynchronous code. Skipping the await
, discarding the returned task, and blocking on the returned task are all unusual scenarios. This guideline is commonly referred to as async
all the way.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论