【问题标题】:android BluetoothDevice.getName() return nullandroid BluetoothDevice.getName() 返回 null
【发布时间】:2014-12-05 02:31:12
【问题描述】:

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

【问题讨论】:

  • 你为什么不做一些空值检查?
  • 当然,我可以检查它是否为空。但是空名称对我来说没用。而且它不是 awlays 为空,所以有一些 api 来刷新设备名称吗?

标签: android bluetooth null device network-scan


【解决方案1】:

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

【讨论】:

  • 用户在我们的ble设备(手表等)中看不到蓝牙地址,只知道设备的蓝牙名称。因此,当 getName() 返回 null 时,选择要连接的确切设备是个问题。
【解决方案2】:

终于找到了解决办法:

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

【讨论】:

  • 如何从 gatt 特征中获取设备名称?可以举个例子吗
  • 提供的链接无效,您可以检查一下吗? @maonanyue
  • “从服务 org.bluetooth.service.generic_access 的 gatt 特征 org.bluetooth.characteristic.gap.device_name 读取设备名称”到底是什么意思。
  • 连接后,您应该进行服务发现,然后设备名称特征将出现在通用访问服务的 BluetoothGatt 中。该服务是必需的,DeviceName 特征条目也是如此。我不能在评论中发布代码。
【解决方案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();
    }

【讨论】:

  • getDeviceName 方法对于 ScanRecord 类不存在。你能发布一个示例代码吗?
  • 这有时仍然可以返回 null。
  • 如果设备没有在广告或扫描响应中公开其名称,它将返回 null。 BTLE 规范中没有要求设备在广告中公开名称。
【解决方案4】:

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

注意,在提供的 sn-p 中,封闭类实现了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);
        }
    }

【讨论】:

    【解决方案5】:

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

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

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

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

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

    【讨论】:

      【解决方案6】:

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

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

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

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

      【讨论】:

        【解决方案7】:

        对于尚未找到解决方案的人。

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

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2012-06-22
          • 2012-05-28
          • 2021-07-07
          • 2013-06-27
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-05-30
          相关资源
          最近更新 更多