android - Xamarin.Android 在直接启动模式下不接收位置更新
问题描述
TLDR:是否可以在直接启动模式下访问位置?
我想在手机启动时接收设备的位置更新(在 Pixel 3a 上测试)。这是常规应用程序的配套功能,因此假设已处理所需的权限授予等。
以下代码也取自从 MainActivity 调用时可以正常工作的组件。
设置位置回调的步骤如下:
首先,监听 ACTION_LOCKED_BOOT_COMPLETED 的 DirectBootAware BroadcastReceiver 意味着我可以在设备启动时和屏幕解锁之前运行代码。
[BroadcastReceiver(Enabled = true, Exported = true, DirectBootAware = true)]
[IntentFilter(new string[] { Intent.ActionLockedBootCompleted })]
public class StartForegroundServiceReceiver : BroadcastReceiver
{
public override void OnReceive(Context context, Intent intent)
{
从这里我可以验证是否授予了 Corse 和 Fine 位置的权限(它们是!)
var permissionCheckResult = context.CheckSelfPermission(Manifest.Permission.AccessFineLocation);
Android.Util.Log.Info("TestingTag", $"AccessFineLocation CheckSelfPermission {permissionCheckResult.ToString()}");
permissionCheckResult = context.CheckSelfPermission(Manifest.Permission.LocationHardware);
Android.Util.Log.Info("TestingTag", $"LocationHardware CheckSelfPermission {permissionCheckResult.ToString()}");
permissionCheckResult = context.CheckSelfPermission(Manifest.Permission.AccessCoarseLocation);
Android.Util.Log.Info("TestingTag", $"AccessCoarseLocation CheckSelfPermission {permissionCheckResult.ToString()}");
并且启用了网络、GPS 和被动位置提供程序(同样,它们都已启用!)
var locationManager = (LocationManager)context.GetSystemService(Context.LocationService);
var enabled = locationManager.IsProviderEnabled(LocationManager.PassiveProvider);
Log.Info("TestingTag", $"PassiveProvider IsProviderEnabled {(enabled ? "yes" : "no")}");
enabled = locationManager.IsProviderEnabled(LocationManager.GpsProvider);
Log.Info("TestingTag", $"GpsProvider IsProviderEnabled {(enabled ? "yes" : "no")}");
enabled = locationManager.IsProviderEnabled(LocationManager.NetworkProvider);
Log.Info("TestingTag", $"NetworkProvider IsProviderEnabled {(enabled ? "yes" : "no")}");
如果我检查GetLastKnownLocation
var location = locationManager.GetLastKnownLocation(LocationManager.GpsProvider);
if (location == null)
{
location = locationManager.GetLastKnownLocation(LocationManager.NetworkProvider);
}
if (location == null)
{
location = locationManager.GetLastKnownLocation(LocationManager.PassiveProvider);
}
if (location == null)
Log.Error("TestingTag", $"Location is null");
这总是返回 null,可能是因为设备刚刚启动?或者可能是因为与未收到任何位置更新相同的根本原因?
最后,我使用LocationManager
var callback = new LocationCallback();
Log.Info("TestingTag", "Starting RequestLocationUpdates via PassiveProvider..");
locationManager.RequestLocationUpdates(LocationManager.PassiveProvider, 3000, 3, callback);
Log.Info("TestingTag", "Starting RequestLocationUpdates via GpsProvider..");
locationManager.RequestLocationUpdates(LocationManager.GpsProvider, 3000, 3, callback);
Log.Info("TestingTag", "Starting RequestLocationUpdates via NetworkProvider..");
locationManager.RequestLocationUpdates(LocationManager.NetworkProvider, 3000, 3, callback);
这是位置回调代码:
public class LocationCallback : ScanCallback, Android.Locations.ILocationListener
{
public void OnLocationChanged(Location location)
{
Log.Info("TestingTag", $"OnLocationChanged {location.Latitude} ~ {location.Longitude}");
}
public void OnProviderDisabled(string provider)
{
Log.Info("TestingTag", $"OnProviderDisabled {provider}");
}
public void OnProviderEnabled(string provider)
{
Log.Info("TestingTag", $"OnProviderEnabled {provider}");
}
public void OnStatusChanged(string provider, [GeneratedEnum] Availability status, Bundle extras)
{
Log.Info("TestingTag", $"OnStatusChanged {provider} ~ {status.ToString()}");
}
}
回调永远不会被调用:-(
我添加了一个BluetoothManager
用于扫描 BLE 信号(省略了代码)并且可以正常工作,ScanResults 可以正常返回,并且电话 / BroadcastReceiver 仍然存在。所以这并不是说 BroadcastReceiver 正在死去......
任何提示/建议/或确认我正在尝试做的事情是不可能的,我们将不胜感激!
谢谢!
解决方案
LocationManager
我能够在我的 Android Q 设备上以 DirectBoot 模式获得有效位置。我是从 aService
而不是 a打电话的BroadcastReceiver
,尽管我认为这不会有什么不同。
这是 my 的清单条目Service
:
<service android:name=".IncomingMessageService"
android:directBootAware="true"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
和我的 Kotlin 代码:
suspend fun getLocation(context: Context): Location? {
return suspendCoroutine { continuation ->
context.getSystemService<LocationManager>()?.run {
val criteria = Criteria().apply {
accuracy = Criteria.ACCURACY_FINE
isAltitudeRequired = false
isBearingRequired = false
isSpeedRequired = false
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
getBestProvider(criteria, true)?.let {
getCurrentLocation(
it,
null,
context.mainExecutor,
{
continuation.resume(it)
})
} ?: continuation.resume(null)
} else {
@Suppress("DEPRECATION")
requestSingleUpdate(
criteria,
{
continuation.resume(it)
},
null
)
}
} ?: continuation.resume(null)
}
}
请注意,requestSingleUpdate()
在 API 30 中已弃用并替换为新的 call getCurrentLocation()
。
另请注意,使用LocationServices
Google Play 服务(而不是 LocationManager)对我来说在 DirectBoot 模式下不起作用。它抱怨“LocationServices.API 在此设备上不可用”,尽管登录后它工作正常。
请务必查看使用后台位置访问的要求和限制。
推荐阅读
- django - Django如何访问单选按钮选定的值
- python-3.x - 在 PyQt/PySide 中运行异步方法而不阻塞 GUI 的简单方法
- python - Boto3 / MLflow 模型通过临时 AWS 凭证进行日志记录
- pyomo - Pyomo:带有 if 子句的约束来检查索引
- xml - 如何解码 XSLT 中 XPath 的节点值?
- error-handling - 带有 CircuitBreaker 的 Apache Camel 3.7.3 日志堆栈跟踪
- javascript - 如何一键将百分比提高 1%
- python - 使用 Python 对 COVID-19 URL 数据的 groupby 和 melt 函数
- sql-server - 存储过程返回负值
- java - 为什么 Spark map 函数中的这段代码仍然被缓存,即使在那里引用的类已经改变?