首页 > 解决方案 > 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 正在死去......

任何提示/建议/或确认我正在尝试做的事情是不可能的,我们将不胜感激!

谢谢!

标签: androidxamarin.androidlocation

解决方案


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()

另请注意,使用LocationServicesGoogle Play 服务(而不是 LocationManager)对我来说在 DirectBoot 模式下不起作用它抱怨“LocationServices.API 在此设备上不可用”,尽管登录后它工作正常。

请务必查看使用后台位置访问的要求和限制


推荐阅读