需要为 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 上的蓝牙低功耗扫描启用位置的主要内容,如果未能解决你的问题,请参考以下文章

Android 低功耗蓝牙开发(扫描连接)

Android 低功耗蓝牙开发(扫描连接)

Android 低功耗蓝牙开发(扫描连接)

Qt低功耗蓝牙系列三(低功耗蓝牙客户端的程序设计纯Android代码)

Qt低功耗蓝牙系列三(低功耗蓝牙客户端的程序设计纯Android代码)

Android 4.4:低功耗蓝牙;无需扫描 BLE 设备即可连接