本文介绍如何使用 .NET Multi-platform App UI (.NET MAUI) 接口。 此接口提供用于检索设备的当前地理位置坐标的 API。
IGeolocation
接口的默认实现可通过 Geolocation.Default 属性获得。
IGeolocation
接口和 Geolocation
类都包含在 Microsoft.Maui.Devices.Sensors
命名空间中。
开始吧
若要访问 地理位置 功能,需要以下特定于平台的设置:
必须指定粗略 或 精细的位置权限,或者同时具有这两种权限,并且应在 Android 项目中进行配置。
此外,如果应用面向 Android 5.0(API 级别 21)或更高版本,则必须声明应用使用清单文件中的硬件功能。 可通过以下方式添加此项:
添加基于程序集的权限:
打开 Platforms/Android/MainApplication.cs 文件,并在
using
指令后添加以下程序集属性:[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)]
如果应用程序面向 Android 10 - Q (API 级别 29 或更高版本),并且正在请求
LocationAlways
,则还必须添加此权限请求:[assembly: UsesPermission(Android.Manifest.Permission.AccessBackgroundLocation)]
- 或 -
更新 Android Manifest:
打开 平台/Android/AndroidManifest.xml 文件,并在
manifest
节点中添加以下内容:<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_FINE_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" />
如果应用程序面向 Android 10 - Q (API 级别 29 或更高版本),并且正在请求
LocationAlways
,则还必须添加此权限请求:<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
- 或 -
在清单编辑器中更新 Android 清单:
在 Visual Studio 中,双击 平台/Android/AndroidManifest.xml 文件以打开 Android 清单编辑器。 然后,在 “所需权限 ”下检查上面列出的权限。 这将自动更新 AndroidManifest.xml 文件。
小窍门
请务必阅读 有关后台位置更新的 Android 文档,因为需要考虑许多限制。
获取最后一个已知位置
设备可能已缓存设备的最新位置。
GetLastKnownLocationAsync()使用此方法访问缓存的位置(如果可用)。 这通常比执行完整位置查询更快,但可能不太准确。 如果不存在缓存位置,此方法将 null
返回 。
注释
如有必要,地理位置 API 会提示用户获取权限。
下面的代码示例演示如何检查缓存的位置:
public async Task<string> GetCachedLocation()
{
try
{
Location ___location = await Geolocation.Default.GetLastKnownLocationAsync();
if (___location != null)
return $"Latitude: {___location.Latitude}, Longitude: {___location.Longitude}, Altitude: {___location.Altitude}";
}
catch (FeatureNotSupportedException fnsEx)
{
// Handle not supported on device exception
}
catch (FeatureNotEnabledException fneEx)
{
// Handle not enabled on device exception
}
catch (PermissionException pEx)
{
// Handle permission exception
}
catch (Exception ex)
{
// Unable to get ___location
}
return "None";
}
根据设备,并非所有位置值都可用。 例如,该 Altitude 属性可以是 null
,值为 0,或者有一个正值表示高于海平面的米数。 可能不存在的其他值包括 Speed 和 Course 属性。
获取当前位置
虽然检查设备的最后已知位置可能更快,但它可能不准确。 GetLocationAsync使用该方法查询设备的当前位置。 可以配置查询的准确性和超时。 最好使用带有 GeolocationRequest 和 CancellationToken 参数的方法重载,因为获取设备的位置可能需要一些时间。
注释
如有必要,地理位置 API 会提示用户获取权限。
下面的代码示例演示如何请求设备的位置,同时支持取消:
private CancellationTokenSource _cancelTokenSource;
private bool _isCheckingLocation;
public async Task GetCurrentLocation()
{
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}");
}
// Catch one of the following exceptions:
// FeatureNotSupportedException
// FeatureNotEnabledException
// PermissionException
catch (Exception ex)
{
// Unable to get ___location
}
finally
{
_isCheckingLocation = false;
}
}
public void CancelRequest()
{
if (_isCheckingLocation && _cancelTokenSource != null && _cancelTokenSource.IsCancellationRequested == false)
_cancelTokenSource.Cancel();
}
并非所有位置值都可用,具体取决于设备。 例如,该 Altitude 属性可以是 null
,值为 0,或者有一个正值表示高于海平面的米数。 可能不存在的其他值包括 Speed 和 Course。
警告
GetLocationAsync 在某些情况下可以返回 null
。 这表示基础平台无法获取当前位置。
侦听位置更改
除了查询设备的当前位置外,还可以在应用处于前台时侦听位置更改。
若要查看应用当前是否正在侦听位置更改, IsListeningForeground 可以查询的属性。 准备好开始侦听位置更改后,应调用该方法 StartListeningForegroundAsync 。 此方法开始侦听位置更新,并在位置更改时引发 LocationChanged 事件,前提是应用位于前台。 GeolocationLocationChangedEventArgs此事件附带的对象具有一个Location类型属性Location,表示检测到的新位置。
注释
如有必要,地理位置 API 会提示用户获取权限。
下面的代码示例演示如何侦听位置更改,以及如何处理更改的位置:
async void OnStartListening()
{
try
{
Geolocation.LocationChanged += Geolocation_LocationChanged;
var request = new GeolocationListeningRequest((GeolocationAccuracy)Accuracy);
var success = await Geolocation.StartListeningForegroundAsync(request);
string status = success
? "Started listening for foreground ___location updates"
: "Couldn't start listening";
}
catch (Exception ex)
{
// Unable to start listening for ___location changes
}
}
void Geolocation_LocationChanged(object sender, GeolocationLocationChangedEventArgs e)
{
// Process e.Location to get the new ___location
}
可以通过为事件注册事件处理程序 ListeningFailed 来实现错误处理。 此事件附带的对象具有一个指示侦听失败原因的GeolocationListeningFailedEventArgs属性,类型为Error。 ListeningFailed引发事件时,对位置进一步更改的侦听将停止,并且不会引发更多LocationChanged事件。
若要停止侦听位置更改,请调用 StopListeningForeground 方法:
void OnStopListening()
{
try
{
Geolocation.LocationChanged -= Geolocation_LocationChanged;
Geolocation.StopListeningForeground();
string status = "Stopped listening for foreground ___location updates";
}
catch (Exception ex)
{
// Unable to stop listening for ___location changes
}
}
注释
当应用未侦听位置更改时,该方法 StopListeningForeground 不起作用。
检查位置服务是否已启用
该 Geolocation 类具有一个只读 IsEnabled
属性,可用于确定是否已在设备上启用位置服务。
准确性
以下部分概述了每个平台的位置准确性距离:
重要
iOS 对准确性有一些限制。 有关详细信息,请参阅 “平台差异 ”部分。
最低
平台 | 距离(以米为单位) |
---|---|
安卓 | 500 |
iOS | 3000 |
Windows操作系统 | 1000 - 5000 |
低
平台 | 距离(以米为单位) |
---|---|
安卓 | 500 |
iOS | 1000 |
Windows操作系统 | 300 - 3000 |
中(默认)
平台 | 距离(以米为单位) |
---|---|
安卓 | 100 - 500 |
iOS | 100 |
Windows操作系统 | 30-500 |
高
平台 | 距离(以米为单位) |
---|---|
安卓 | 0 - 100 |
iOS | 10 |
Windows操作系统 | <= 10 |
最佳
平台 | 距离(以米为单位) |
---|---|
安卓 | 0 - 100 |
iOS | ~0 |
Windows操作系统 | <= 10 |
检测模拟位置
某些设备可能会从提供商或提供模拟位置的应用程序获取位置数据。 您可以通过在任意IsFromMockProvider上使用Location来检测这一情况:
public async Task CheckMock()
{
GeolocationRequest request = new GeolocationRequest(GeolocationAccuracy.Medium);
Location ___location = await Geolocation.Default.GetLocationAsync(request);
if (___location != null && ___location.IsFromMockProvider)
{
// ___location is from a mock provider
}
}
两个位置之间的距离
该方法 CalculateDistance 计算两个地理位置之间的距离。 此计算距离不考虑道路或其他路径,只是地球表面两点之间的最短距离。 此计算称为 大圆距离 计算。
以下代码计算美国波士顿和旧金山城市之间的距离:
Location boston = new Location(42.358056, -71.063611);
Location sanFrancisco = new Location(37.783333, -122.416667);
double miles = Location.CalculateDistance(boston, sanFrancisco, DistanceUnits.Miles);
构造 Location(Double, Double, Double) 函数分别接受纬度和经度参数。 正纬度值位于赤道以北,正经度值位于 Prime Meridian 的东部。 使用最终参数指定 CalculateDistance
英里或公里。 该 UnitConverters 类还定义了用于在两个单位之间转换的 KilometersToMiles 和 MilesToKilometers 方法。
平台差异
本部分描述了与地理位置 API 相关的各个平台特定差异。
在每个平台上以不同的方式计算海拔高度。
在 Android 系统中,如果可用,高度以米为单位返回,并测量为高于 WGS 84 参考椭球体的高度。 如果此位置没有海拔高度, 0.0
则返回。
该 Location.ReducedAccuracy 属性仅供 iOS 使用,并在所有其他平台上返回 false
。