android BluetoothDevice.getName() возвращает ноль
Иногда BluetoothDevice.getName() возвращает значение NULL. Как я могу это исправить? remoteDeviceName может быть нулевым в следующем коде. И мне нужно различать мое устройство и другие устройства по 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);
});
6 ответов
Наконец, я нашел решение:
1.Для подключения устройства:
Считайте имя устройства из характеристики org.bluetooth.characteristic.gap.device_name.xml службы org.bluetooth.service.generic_access.xml.
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;
}
}
BluetoothDevice.getName()
может вернуться null
если имя не может быть определено. Это может быть связано с любым количеством факторов. Независимо от того, имя является дружественным именем устройства и не должно использоваться, чтобы отличать его от других устройств. Вместо этого используйте аппаратный адрес через getAddress()
,
Я знаю, что это старый, но этот более ориентированный на спецификации ответ может помочь ответить на некоторые случаи.
В Bluetooth Low Energy рекламные данные и данные ответов на сканирование должны иметь только адрес Bluetooth. Данные объявления - это то, как клиентская конечная точка BTLE обнаруживает сервисное устройство. Клиент может запросить ответ сканирования и получить больше данных. Имя устройства необязательно в этих данных. Однако спецификация BTLE требует, чтобы все конечные точки с низким энергопотреблением Bluetooth поддерживали услугу общего доступа, которая требуется для поддержки характеристики имени устройства. К сожалению, чтобы прочитать эту характеристику, Android должен сначала подключиться и выполнить обнаружение службы. Если в ответе рекламы / сканирования не указана информация, я не верю, что Android подключается к устройству, чтобы получить имя. По крайней мере, я никогда не видел никаких признаков подключения без приложения, специально запрашивающего подключение. Это не то, что вы хотите сделать, если хотите принять решение о подключении.
К счастью, большинство устройств BTLE, с которыми я работал, предоставляют свое имя в рекламе или ответе сканирования.
Другая возможность состоит в том, что устройство может поместить имя в ответную часть сканирования рекламы. В зависимости от того, как настроен Android-сканер BTLE, можно получить только рекламные данные, а не ответ на сканирование. В этом случае имя не будет найдено, если устройство добавит его в ответ на сканирование. Однако настройки сканера по умолчанию таковы, что ответ о сканировании должен быть получен до того, как данные сканирования будут переданы в приложение.
На зефир, утилизировать 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();
}
Я пытался отобразить имя моего модуля Bluetooth RN4020 и столкнулся с той же проблемой. Нашел проблему в форуме Микрочипа:
Если вы включили частную службу или MLDP, максимальные байты имени устройства составляют 6 байтов из-за ограничения полезной нагрузки объявления в 31 байт.
Я установил имя устройства на 9 символов. Установка имени в 4 байта устранила проблему.
Если вы узнаете UUID своих пользовательских сервисов и знаете, что это ваше устройство, вы также можете подключиться к устройству и прочитать его имя (если оно длиннее 6 байт в моем случае). Это также было предложено на форуме по микрочипам.
Я обнаружил, что если вы запрашиваете имя устройства сразу после его сканирования, оно может вернуть ноль. Чтобы обойти это, я опрашиваю работоспособный объект каждую секунду или около того в потоке пользовательского интерфейса максимум 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);
}
}
Для кого-то не нашел решения.
Рекламный пакет Bluetooth имеет максимальный размер 31 байт. Итак, если у вашего устройства длинное имя. Его можно обрезать. См. https://devzone.nordicsemi.com/f/nordic-q-a/14/what-s-the-maximum-size-for-an-advertisement-package
Если вы хотите получить правильное имя устройства Bluetooth (даже длинное), не используйте
startLeScan()
. Вместо этого, используя методstartDiscovery()
. Google сказал: "Процесс обнаружения обычно включает сканирование запроса продолжительностью около 12 секунд, за которым следует сканирование страницы каждого обнаруженного устройства для получения его имени Bluetooth". Я пробовал, и это работает как шарм. См. https://developer.android.com/guide/topics/connectivity/bluetooth.