14

有时,BluetoothDevice.getName() 返回 null。我该如何解决?remoteDeviceName 在以下代码中可能为 null。我需要通过 remoteDeviceName 区分我的设备和其他设备。

BluetoothAdapter.getDefaultAdapter().startLeScan(new LeScanCallback() {
            @Override
            public void onLeScan(final BluetoothDevice device, final int rssi,
                    byte[] scanRecord) {
                    String remoteDeviceName = device.getName();
                  Log.d("Scanning", "scan device " + remoteDeviceName);
            });
4

7 回答 7

16

最后,我找到了解决方案:

1.对于连接的设备:

从服务org.bluetooth.service.generic_access的 gatt 特征org.bluetooth.characteristic.gap.device_name读取设备名称。

2.对于未连接的设备:

    /**
     * Get device name from ble advertised data
     */
    private LeScanCallback mScanCb = new LeScanCallback() {
        @Override
        public void onLeScan(final BluetoothDevice device, final int rssi,
            byte[] scanRecord) {
            final BleAdvertisedData badata = BleUtil.parseAdertisedData(scanRecord);
            String deviceName = device.getName();
            if( deviceName == null ){
                deviceName = badata.getName();
            }
    }


////////////////////// Helper Classes: BleUtil and BleAdvertisedData ///////////////
    final public class BleUtil {        
        private final static String TAG=BleUtil.class.getSimpleName();
        public static BleAdvertisedData parseAdertisedData(byte[] advertisedData) {      
            List<UUID> uuids = new ArrayList<UUID>();
            String name = null;
            if( advertisedData == null ){
                return new BleAdvertisedData(uuids, name);
            }

            ByteBuffer buffer = ByteBuffer.wrap(advertisedData).order(ByteOrder.LITTLE_ENDIAN);
            while (buffer.remaining() > 2) {
                byte length = buffer.get();
                if (length == 0) break;

                byte type = buffer.get();
                switch (type) {
                    case 0x02: // Partial list of 16-bit UUIDs
                    case 0x03: // Complete list of 16-bit UUIDs
                        while (length >= 2) {
                            uuids.add(UUID.fromString(String.format(
                                    "%08x-0000-1000-8000-00805f9b34fb", buffer.getShort())));
                            length -= 2;
                        }
                        break;
                    case 0x06: // Partial list of 128-bit UUIDs
                    case 0x07: // Complete list of 128-bit UUIDs
                        while (length >= 16) {
                            long lsb = buffer.getLong();
                            long msb = buffer.getLong();
                            uuids.add(new UUID(msb, lsb));
                            length -= 16;
                         }
                        break;
                    case 0x09:
                        byte[] nameBytes = new byte[length-1];
                        buffer.get(nameBytes);
                        try {
                            name = new String(nameBytes, "utf-8");
                        } catch (UnsupportedEncodingException e) {
                            e.printStackTrace();
                        }
                        break;
                    default:
                        buffer.position(buffer.position() + length - 1);
                        break;
                    }
                }
            return new BleAdvertisedData(uuids, name);
        }
    }


    public class BleAdvertisedData {
        private List<UUID> mUuids;
        private String mName;
        public BleAdvertisedData(List<UUID> uuids, String name){
            mUuids = uuids;
            mName = name;
        }

        public List<UUID> getUuids(){
            return mUuids;
        }

        public String getName(){
            return mName;
        }
    }
于 2014-10-14T13:09:38.663 回答
7

BluetoothDevice.getName()null如果无法确定名称,可能会返回。这可能是由于许多因素造成的。无论如何,该名称是设备的友好名称,不应用于将其与其他设备区分开来。相反,使用硬件地址通过getAddress().

于 2014-10-10T03:09:26.670 回答
4

我知道这很旧,但这个更面向规范的答案可能有助于回答某些情况。

在低功耗蓝牙中,广告和扫描响应数据只需要具有蓝牙地址即可。广告数据是客户端 BTLE 端点发现服务设备的方式。客户端可以请求扫描响应并获取更多数据。设备名称在此数据中是可选的。但是,BTLE 规范要求所有低功耗蓝牙端点都支持支持设备名称特性所需的通用访问服务。不幸的是,要读取该特性,Android 必须首先连接并进行服务发现。如果广告/扫描响应未提供信息,我不相信 Android 连接到设备以获取名称。至少我从来没有看到任何没有应用程序特别请求连接的连接迹象。

幸运的是,我使用过的大多数 BTLE 设备确实在广告或扫描响应中提供了它们的名称。

另一种可能性是设备可以将名称放在广告的扫描响应部分中。根据设置 Android 的 BTLE 扫描仪的方式,可能只获得广告数据而不是扫描响应。在这种情况下,如果设备将其放入扫描响应中,将找不到该名称。但是,默认扫描仪设置是必须在扫描数据被传递到应用程序之前接收扫描响应。

于 2018-06-27T23:38:13.527 回答
2

在 Marshmallow 上,ScanRecord.getDeviceName()用于检索嵌入在扫描记录中的本地名称。

BluetoothDevice.getName()如果本地名称包含在扫描响应中而不是立即广告数据包中,则它是不可靠的。

    @Override
    public void onScanResult(int callbackType, ScanResult scanResult) {
        super.onScanResult(callbackType, scanResult);

        // Retrieve device name via ScanRecord.
        String deviceName = scanResult.getScanRecord().getDeviceName();
    }
于 2016-10-05T21:55:33.277 回答
1

我试图显示我的 RN4020 蓝牙模块的名称并遇到了同样的问题。在 Microchip 的论坛中发现问题:

如果您启用了私有服务或 MLDP,由于 31 字节的广告负载限制,设备名称的最大字节为 6 个字节。

我已将设备名称设置为 9 个字符。将名称设置为 4 个字节可解决此问题。

如果您识别自定义服务的 UUID,因此您知道它是您的设备,您还可以连接到设备并读取它的名称(如果它在我的情况下超过 6 个字节)。这也在 Microchips 论坛中提出。

http://www.microchip.com/forums/m846328.aspx

于 2017-11-27T10:24:51.897 回答
0

我发现,如果您在扫描后立即查询设备名称,它可能会返回 null。为了解决这个问题,我每隔一秒左右在 UI 线程上轮询一个可运行对象,最多 3 次(所以 3 秒),然后通常会解析名称。

请注意,在提供的代码段中,封闭类 implements Runnable,因此我可以this传入View.postDelayed(Runnable action, long delayMillis)

    private static final int MAX_NAME_CHECKS = 3;
    private static final int NAME_CHECK_PERIOD = 1000;

    int nameChecks;

    @Override
    public void run() {
        resolveName();
    }

    /**
     * Checks for the device name, for a maximum of {@link ViewHolder#MAX_NAME_CHECKS}
     * as the name may not have been resolved at binding.
     */
    private void resolveName() {
        if (device != null) {
            String name = device.getName();
            boolean isEmptyName = TextUtils.isEmpty(name);

            if (isEmptyName) deviceName.setText(R.string.unknown_device);
            else deviceName.setText(name);

            // Check later if device name is resolved
            if (nameChecks++ < MAX_NAME_CHECKS && isEmptyName)
                itemView.postDelayed(this, NAME_CHECK_PERIOD);
        }
    }
于 2017-03-12T22:22:47.650 回答
0

因为有人没有找到解决方案。

  1. 蓝牙广告包的最大大小为 31 字节。因此,如果您的设备名称很长。它可以被截断。请参阅:https ://devzone.nordicsemi.com/f/nordic-qa/14/what-s-the-maximum-size-for-an-advertisement-package

  2. 如果您确实想获得正确的蓝牙设备名称(甚至是长名称),请不要使用startLeScan(). 取而代之的是,使用 method startDiscovery()。谷歌表示:“发现过程通常涉及约 12 秒的查询扫描,然后对找到的每个设备进行页面扫描以检索其蓝牙名称。” 我试过了,它就像一个魅力。请参阅:https ://developer.android.com/guide/topics/connectivity/bluetooth

于 2020-06-13T04:54:01.723 回答