需要为 Android 6.0 上的蓝牙低功耗扫描启用位置
Posted
技术标签:
【中文标题】需要为 Android 6.0 上的蓝牙低功耗扫描启用位置【英文标题】:Location needs to be enabled for Bluetooth Low Energy Scanning on Android 6.0 【发布时间】:2016-01-07 20:01:11 【问题描述】:升级到 android 6.0 版后,蓝牙低功耗 (BLE) 扫描仅在设备上启用了定位服务的情况下才能工作。参考这里:Bluetooth Low Energy startScan on Android 6.0 does not find devices
基本上,您需要为应用和手机启用权限。这是一个错误吗?是否可以在没有实际启用定位服务的情况下进行扫描?我不想为我的所有应用程序提供位置。
编辑
我没有提到我正在使用 API 21 中提供的BluetoothLeScanner
中的startScan()
方法。我可以接受此方法所需的清单中的课程和精细位置权限。我只是不希望我的应用程序的用户必须在他们的设备(GPS 等)上启用位置服务才能使用我的应用程序。
以前,startScan()
方法将在手机上禁用定位服务的情况下运行并返回结果。然而,在 Marshmallow 上,当手机上未启用定位服务且课程/精细定位权限仍在清单中时,相同的应用程序会“扫描”但静默失败并且不返回任何结果。
【问题讨论】:
您使用的是什么设备?我在使用 Moto G 第二代时遇到了同样的问题。 Moto G 第 1 代和 Nexus 6 使用完全相同的代码运行良好,无需显式启用定位服务。 我在任何运行 Marshmallow 的设备上都发现了它 - Nexus 5X、Samsung S6、Samsung S7、LG G4 【参考方案1】:好吧,我查看了用 Eclipse 编写的代码,并在那里使用了 startScan (API 21) 函数,而没有在清单文件中声明位置内容。我仍然得到正确的回调。 您是否尝试过在没有位置声明的情况下运行代码? 另一方面 - 您可以使用不需要这些权限的已弃用 startLeScan (API 18)。但是,在我看来,使用 API 18 方法在服务中搜索和读取所需特征更加复杂。
【讨论】:
我正在使用BluetoothLeScanner
中的startScan
函数。我有目的地使用未弃用的方法。事实上,我正在检查使用大于 21 的 API 的设备,以专门使用所提供的新方法。我试过没有这个位置,它只是默默地失败了。扫描运行但没有返回任何内容(使用 post-API 21 方法)【参考方案2】:
我也在清单上试过这个,但没有请求许可,不知道为什么。您的应用程序是否在启动时提示位置权限?如果不是,我们需要request for permission on runtime。
您也可以检查一下以测试您的应用是否正常运行:
打开设置 > 应用程序 > YourApplication > 权限 并启用位置,然后尝试扫描结果。
仅当您在清单上提供了 ACCESS_COARSE_LOCATION 时,才会在此处列出位置。
【讨论】:
是的,您必须在运行时明确请求权限,并为设备开启定位服务。但我想知道是否有一种方法可以进行 BLE 扫描,而无需在运行时打开位置服务或请求课程位置权限。 请求权限不够,还需要检查定位服务是否开启。 为什么谷歌允许这个问题?说实话我不懂【参考方案3】:您可以使用BluetoothAdapter.startDiscovery()
。
它将扫描蓝牙智能设备和经典蓝牙设备,但不需要启用定位服务。
(在 Android 6 上您仍需要 ACCESS_COARSE_LOCATION
权限。)
您可以在找到的设备上致电BluetoothDevice.getType
以过滤蓝牙智能/低功耗设备。
【讨论】:
【参考方案4】:我发现在 Android 6 之后你必须授予 ACCESS_COARSE_LOCATION 权限。但在某些设备上,还需要打开您的手机定位服务 (GPS),以便您可以发现外围设备。我发现使用带有 Android 7.0 的 Nexus 5x。
【讨论】:
Nexus 5x 和 Android 6 对我来说也是如此 在 Moto G5 plus 上与 Android 7.1.1 相同【参考方案5】:我通过在 Gradle 文件中将 targetSdkVersion
设置为 22 解决了这个问题。
您必须在清单中声明ACCESS_COARSE_LOCATION
,但即使用户从应用设置中拒绝此权限,BLE 扫描也会起作用。
这只是避免请求位置许可的一种技巧。最好针对最新的 android 版本。
编辑
不应再使用此解决方案,因为 Google Play 要求新应用至少面向 Android 8.0(API 级别 26)。应用应请求位置权限以进行 BLE 扫描。
【讨论】:
太好了,谢谢!您是否知道如何处理模拟 AltBeacon 等监控的库?没有位置就无法工作:(并且它们使用基本相同的机制......那么最小编译版本呢?@JiTHiN 从 23 降级到 22,它甚至可以在 Android 7.0 上运行。我最初的问题是您必须启用本地化,现在我可以扫描禁用本地化的 BLE 设备。 这是一个 hack,但这是不推荐最好针对最新的 sdk 版本。正确的做法是在运行时请求ACCESS_COARSE_LOCATION
这不再是一个解决方案(请参阅developer.android.com/distribute/best-practices/develop/…“从 2018 年 8 月 1 日起,Google Play 将要求新应用至少面向 Android 8.0(API 级别 26),并且应用更新面向 Android 8.0 从 2018 年 11 月 1 日开始。”)【参考方案6】:
不,这不是错误。
这个issue 被提交给 Google,他们回复说这是预期的行为,他们不会修复它。他们将开发人员引导至this site,其中指出硬件标识符访问现在需要位置许可。现在开发者有责任让他们的用户意识到这个要求。
然而,在这个问题中,它没有说明为什么需要定位服务(GPS 等),而且他们似乎不会重新讨论这个问题来解释这一点,因为它已被标记为预期行为。
回答问题的第二部分:是的,可以在不启用定位服务的情况下进行扫描。您可以使用BluetoothAdapter.getDefaultAdapter().startDiscovery()
进行蓝牙经典扫描,这将在定位服务关闭的情况下工作。这将发现所有蓝牙设备、BLE 和其他设备。但是,如果 BLE 设备被视为startScan()
的结果,它们将不会拥有扫描记录。
【讨论】:
谷歌声称这不是一个错误让我感到很冷。他们的态度是简单的废话。 这不是一个真正的错误 - 如果恶意应用程序开发人员可以扫描已知的蓝牙信标,那么他们可以通过询问位置权限来找出您的位置,而不会引起用户的怀疑。因此,不幸的是,请求 BLE 访问权限必须与请求您的大致位置相同。 让我看看我有没有这个直。 BLE 的最大优势是“LE”位,即低能量,这意味着减少了我的电池消耗。当然,为了获得这些节省,我必须打开 GPS,这是一种众所周知的电池消耗,因此否定了我可能从使用 BLE 中获得的任何功率节省。 /me 难以置信地摇头。 这太不可思议了。当然,如果 Google 担心 BLE 扫描被用于推断位置,他们应该为此添加单独的权限,而不是通过强制用户允许 GPS 访问来将婴儿和洗澡水一起扔出去! @Pierre-LucPaour 强制开启 GPS 意味着其他实际使用定位服务的应用程序可以在幕后“合法”使用它。用户手动关闭 GPS 的原因之一是为了防止应用程序定期使用它并耗尽电池电量,强制 GPS 搜索蓝牙是让用户知道他们的位置可能被使用的最反直觉的方式。【参考方案7】:从我最近在 android 8.0 上看到的情况来看,不需要打开 GPS 来进行 BLE 扫描,但你必须在清单中声明它,但用户必须允许权限。
当您尝试使用startScan()
方法进行扫描时,Android 会提示用户允许位置权限。如果权限不被允许,您的扫描将失败。
【讨论】:
【参考方案8】:您可以使用 CompanionDeviceManager (API26) 扫描没有位置访问权限的 BLE 设备。 https://developer.android.com/reference/android/companion/CompanionDeviceManager.
【讨论】:
请注意,它在 Android 8 和 9 中不起作用甚至崩溃。我可以在 Pixel 3a 上使用它,但在我的三星测试设备上,系统服务只是崩溃了。所以不幸的是不是一个非常稳定的解决方案。 谢谢@Peterdk。我只是在实验中使用了这个组件。可惜没有好好发展。 当我使用过滤器扫描时,伴侣设备管理器在没有扫描过滤器的情况下崩溃(三星 Android 8),它可以工作。我认为这个功能不是一个稳定的解决方案。文档也无济于事。【参考方案9】:在您将ACCESS_COARSE_LOCATION
添加到清单后,
在运行时请求许可:
public void checkPermission()
if (Build.VERSION.SDK_INT >= 23)
if (checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED && checkSelfPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED)
else
ActivityCompat.requestPermissions(this, new String[]
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION,, 1);
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults)
if (requestCode == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED && grantResults[1] == PackageManager.PERMISSION_GRANTED)
else
checkPermission();
为我工作!
【讨论】:
以上是关于需要为 Android 6.0 上的蓝牙低功耗扫描启用位置的主要内容,如果未能解决你的问题,请参考以下文章
Qt低功耗蓝牙系列三(低功耗蓝牙客户端的程序设计纯Android代码)