安卓 BluetoothDevice.getName() 返回 null。

16

有时候,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);
            });

为什么不进行一些空值检查呢? - Niko Adrianus Yuwono
当然,我可以检查它是否为空。但是对我来说,空名称是无用的。而且它并不总是为空,所以有没有一些API可以刷新设备名称? - maonanyue
7个回答

19

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

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;
        }
    }

1
你如何从Gatt特征获取设备名称?你能给我任何例子吗? - User6006
链接无法使用,能否请检查一下? @maonanyue - MSD
你的意思是什么:“从org.bluetooth.service.generic_access服务的org.bluetooth.characteristic.gap.device_name gatt特征中读取设备名称”? - El Sushiboi
一旦连接成功,您应该进行服务发现,然后在通用访问服务的BluetoothGatt中会出现设备名称特征。此服务是必需的,设备名称特征条目也是如此。不过我无法在评论中发布代码。 - Brian Reinhold

10

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


14
用户无法在我们的BLE设备(手表等)中看到蓝牙地址,只能知道设备的蓝牙名称。因此,当getName()返回null时,选择确切的设备进行连接会出现问题。 - maonanyue

4
我知道这篇文章有些老了,但是这个更专业的回答可能会帮助解决一些问题。
在蓝牙低功耗技术中,广告和扫描响应数据只需要具备蓝牙地址即可。广告数据是客户端BTLE终端设备发现服务设备的方式。客户端可以请求扫描响应并获取更多数据。在这些数据中,设备名称是可选的。然而,BTLE规范要求所有蓝牙低功耗终端支持通用访问服务,该服务必须支持设备名称特征。不幸的是,要读取该特征,Android必须首先连接并进行服务发现。如果广告/扫描响应没有提供信息,我认为Android不会连接到设备以获取名称。至少我从未见过任何连接的迹象,除非应用程序明确请求连接。如果您想做出连接决策,则不希望被要求这样做。
幸运的是,我使用过的大多数BTLE设备都在广告或扫描响应中提供其名称。
另一个可能性是设备可能将名称放在广告的扫描响应部分。根据Android的BTLE扫描器设置方式,可能只会收到广告数据而不是扫描响应。在这种情况下,如果设备将名称放在扫描响应中,则找不到名称。但是,默认的扫描器设置是必须接收到扫描响应后才会将扫描数据传递给应用程序。

3

在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();
    }

ScanRecord类中不存在getDeviceName方法。你能发布一个样例代码吗? - Nagendra Badiganti
2
有时候这仍然可能返回null。 - Bauer Marius
如果设备在广告或扫描响应中没有公开其名称,则它将返回null。BTLE规范中没有要求设备在广告中公开名称。 - Brian Reinhold

1
我试图显示我的RN4020蓝牙模块的名称,并遇到了同样的问题。在Microchip的论坛上找到了问题所在:
如果您启用了私有服务或MLDP,则设备名称的最大字节数为6个字节,由于31个字节的广告负载限制。
我将设备名称设置为9个字符。将名称设置为4个字节可以解决该问题。
如果您识别自定义服务的UUID,以便知道它是您的设备,您也可以连接到设备并读取其名称(如果在我的情况下超过6个字节)。这也是Microchip论坛中提出的建议。

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


0

对于还没有找到解决方案的人。

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

  2. 如果您确实想要获取蓝牙设备的正确名称(即使是长名称),请不要使用startLeScan()。而是使用方法startDiscovery()。 Google表示:“发现过程通常涉及大约12秒的查询扫描,然后是每个找到的设备的页面扫描以检索其蓝牙名称。”我已经尝试过了,它非常有效。 请参见:https://developer.android.com/guide/topics/connectivity/bluetooth


0

我发现如果在扫描后立即查询设备名称,可能会返回 null。为了解决这个问题,我每秒钟轮询一次 UI 线程的可运行项,最多 3 次(即 3 秒),通常在此期间可以解决名称问题。

请注意,在提供的代码片段中,封闭类实现了 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);
        }
    }

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