蓝牙低功耗扫描有时找不到设备

21

我正在扫描蓝牙低功耗设备并作为外围设备运行(在Moto G 2nd Gen上运行Android 6.0)。

我的问题是有时候(似乎是随机的但经常出现),它找不到我的其他外围设备,而其他时候它可以正常工作。

我有一个iOS设备运行类似的代码(都在扫描外围设备并作为外围设备运行),当Android扫描无法找到iOS设备时,我的iOS可以很好地找到充当外围设备的Android设备。因此,这似乎只是一个扫描方面的问题。

它不仅仅是找不到我的iOS设备,而且找不到任何蓝牙设备。当它工作时,它会发现我的iOS设备以及一堆其他设备。

我已经尝试使用和不使用ScanFilters,但得到了相同的问题。 我正在使用SDK 26构建,最低SDK为23。

我设置了所需的权限,因为有时它可以正常工作。

下面是相关代码:

private void startScanning() {
    mHandler = new Handler(mContext.getMainLooper());

    mHandler.postDelayed(new Runnable() {
        @Override
        public void run() {
            ScanSettings settings = new ScanSettings.Builder()
                                        .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
                                        .setReportDelay(0)
                                        .build();

            mBluetoothLeScanner.startScan(null, settings, mScanCallback);
        }
    }, 1000);
}

private ScanCallback mScanCallback = new ScanCallback() {
    @Override
    public void onScanResult(int callbackType, ScanResult result) {
        super.onScanResult(callbackType, result);

        if( result == null || result.getDevice() == null )
            return;

        Log.e("myTest", "Found Device");

        BluetoothDevice device = result.getDevice();
        final String deviceAddress = device.getAddress();

        List<ParcelUuid> parcel = result.getScanRecord().getServiceUuids();

        if (parcel != null) {
            String parcelUUID = parcel.toString().substring(1,37);

            if (parcelUUID.equalsIgnoreCase(mContext.getString(R.string.service_uuid))) {
                final BluetoothDevice bleDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(deviceAddress);

                if (!seenPeripherals.contains(deviceAddress)) {
                    stopScanning();

                    mHandler = new Handler(mContext.getMainLooper());

                    mHandler.postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            Log.e("AppToApp", "Trying to connect to device " + deviceAddress);

                            mGatt = bleDevice.connectGatt(mContext, false, mGattCallback);
                        }
                    }, 1000);
                }
            }
        }   
    }
}

你知道只有在启用了位置服务的情况下才能找到设备吗? - Tim
是的,正如我所说的,它大多数时候都能正常工作,但有时重新运行应用程序后,它会找不到任何设备。如果未启用位置服务,则大多数情况下它将无法工作。关闭应用程序并重新启动即可再次找到设备,但这在发布的应用程序中是不可接受的。 - kdbdallas
如何检测扫描是否发现了任何设备,如果没有,则显示一个对话框,允许用户执行另一次扫描(例如,在对话框中按下按钮),或者只是自动执行另一次扫描。在我们的应用程序中,如果用户没有看到他们期望列出的设备,我们实际上总是为用户提供重新扫描的选项。 - hBrent
有些与@eiran建议的相关性;这可能是由于省电/限速导致的结果。我不熟悉Android,但我知道蓝牙的“Inquiry SCAN”子状态具有相对较高的能量影响,因此可能在某个地方受到了限制。 - MarkM
@eiran,HIGH_PERFORMANCE设置是什么?我目前在进行扫描时使用SCAN_MODE_LOW_LATENCY,因为Android将其指定为“使用最高占空比进行扫描”。 - kdbdallas
显示剩余4条评论
4个回答

6
我遇到了同样的问题。这是因为从 Marshmallow API 23 及更高版本开始,Google政策已经发生了变化,如果想使用BLE,需要打开GPS。查看Google文档,请访问运行时权限链接。要解决这个问题,你需要在手机设置中启用“位置”,并且在应用程序中请求位置权限以使扫描正常工作。必须同时完成这两个步骤。
若要请求位置权限,请在对话框或类似位置添加以下内容:
myActivity.requestPermissions(new String[]{Manifest.permission.ACCESS_COURSE_LOCATION}, yourPermissionRequestCode);

并实现:

@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] Results){
    if(requestCode == yourPermissionRequestCode)
    {
        ... //Do something based on Results
    }
}

myActivity中处理用户选择的任何内容。您还需要执行以下操作才能开启设备的位置服务:

Intent enableLocationIntent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
myActivity.startActivityForResult(enableLocationIntent, yourServiceRequestCode);

你可以通过实现以下内容来检查用户是否打开了位置服务:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
    if(requestCode == yourServiceRequestCode)
    {
        ...//Do whatever you need to
    }
}

myActivity中,您也可以通过以下方式手动打开位置服务:
输入手机设置->选择“应用程序”->选择您的应用程序->选择“权限”选项->打开“位置”权限。
一旦用户启用了权限并启动了位置服务,您就可以开始扫描外设。我注意到,如果您在启用权限/打开位置服务时已经在扫描,则仍不会在onScanResults中显示任何内容。
这使公司能够查看您的位置并将您引导到他们想要的地方。
在扫描设备时,一旦连接到BLE设备,您可以关闭手机上的位置服务,并仍然保持与外围设备的连接。但是,一旦连接到固件(外围设备),则无法再连接到任何新的外围设备,直到断开与配对设备的连接,并且所有其他扫描设备(移动设备)无法在其扫描列表中看到外围设备名称(当用户搜索附近的外围设备时),因为一个外围设备一次只能连接1个设备。
有关BLE基本示例,请查看Truiton Ble教程。我认为这段代码片段将解决您的问题。祝编码愉快 :)

3
问题在于我已经设置了位置权限并请求了它,有时确实可以工作,就像之前提到的那样。问题在于即使我请求了权限,有时(随机地)仍然无法正常工作。如果我不请求权限,它永远不会工作。 - kdbdallas

2
可能是因为Android手机的蓝牙芯片不好。您查看了hci日志或logcat吗?
Bluetooth 4.0芯片(您的moto g中的芯片)有严格的限制,即您不能同时成为中心和外设,尽管扫描应始终允许。因此,在BT 4.0被淘汰之前,我不会推出依赖于Android外设功能的产品。
您应该使用至少具有蓝牙4.1芯片的新手机进行测试。

(当它工作时)我可以同时很好地成为中心和外设。不幸的是,我没有另一台设备(大多数情况下我是iOS开发人员),但我会尝试借用另一台设备,并且我正在努力让同事在其他设备上尝试它。谢谢。 - kdbdallas

1

3
我不明白这可能是问题的原因,因为我正在运行一个使用更新API的设备,它有时可以正常工作。是的,这对于旧设备是必需的,但这并不能解释为什么我的当前代码有时在同一设备上工作,有时又不工作。 - kdbdallas
可能是设备本身的问题。请在其他手机和制造商中尝试您的代码。 - Pedro Varela
以防万一,你的清单文件里有这个吗?<!--如果你想声明你的应用只能在支持BLE的设备上运行,请在应用的清单文件中包含以下内容:false meanwhile--> <uses-feature android:name="android.hardware.bluetooth_le" android:required="true" /> - Pedro Varela

0

如果您使用该应用程序进行非商业目的(即预计少于5K个用户),则可以使用P2Pkit

它在旧版Android上除了BLE和非BLE蓝牙之外还使用P2P wifi连接,并且在发现附近手机方面实际上取得了很好的效果。

优点是它也适用于IOS,并且具有相当简单的界面。

缺点是超过5K个用户您需要付费。


谢谢,但这是用于商业应用程序的,超出了那个限制。 - kdbdallas

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接