android BluetoothDevice.getName() return null
network-scan (6)
Descubrí que si consulta el nombre del dispositivo inmediatamente después de que se lo haya recogido en el escaneo, puede devolver nulo. Para evitar esto, sondeo un ejecutable cada segundo más o menos en el hilo de la interfaz de usuario un máximo de 3 veces (3 segundos), y el nombre generalmente se resuelve para entonces.
Tenga en cuenta que, en el fragmento proporcionado, la clase
Runnable
implementa
Runnable
, de ahí que pueda pasar
this
a
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);
}
}
En algún momento, BluetoothDevice.getName () devuelve nulo. ¿Cómo puedo arreglarlo? remoteDeviceName puede ser nulo en el siguiente código. Y necesito distinguir mi dispositivo y otros dispositivos por 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);
});
En Marshmallow, utilice
ScanRecord.getDeviceName()
para recuperar el nombre local incrustado en el registro de escaneo.
BluetoothDevice.getName()
no es confiable si el nombre local se incluye en una respuesta de exploración, en lugar de en el paquete publicitario inmediato.
@Override
public void onScanResult(int callbackType, ScanResult scanResult) {
super.onScanResult(callbackType, scanResult);
// Retrieve device name via ScanRecord.
String deviceName = scanResult.getScanRecord().getDeviceName();
}
Estaba tratando de mostrar el nombre de mi módulo Bluetooth RN4020 y enfrenté el mismo problema. Encontró el problema en el foro de Microchip:
Si habilitó el servicio privado o MLDP, el número máximo de bytes del nombre del dispositivo es de 6 bytes, debido a la limitación de carga útil de publicidad de 31 bytes.
Había configurado el nombre del dispositivo a 9 caracteres. Establecer el nombre en 4 bytes solucionó el problema.
Si reconoce los UUID de sus servicios personalizados para saber que es su dispositivo, también puede conectarse al dispositivo y leer su nombre (si es mayor de 6 bytes en mi caso). Esto también fue sugerido en el foro de Microchips.
Finalmente, descubrí la solución:
1.Para dispositivo conectado:
Lea el nombre del dispositivo de la característica gatt org.bluetooth.characteristic.gap.device_name of service org.bluetooth.service.generic_access .
2.Para dispositivo no conectado:
/**
* 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;
}
}
Sé que esto es antiguo, pero esta respuesta más orientada a las especificaciones puede ayudar a responder algunos casos.
En Bluetooth Low Energy, los datos publicitarios y de respuesta de escaneo solo se requieren para tener la dirección Bluetooth. Los datos publicitarios muestran cómo un punto final BTLE del cliente descubre un dispositivo de servicio. Un cliente puede solicitar una respuesta de escaneo y obtener más datos. El nombre del dispositivo es opcional en estos datos. Sin embargo, la especificación BTLE requiere que todos los puntos finales Bluetooth de baja energía admitan el servicio de acceso genérico que se requiere para admitir la característica de nombre del dispositivo. Desafortunadamente, para leer esa característica, el Android primero debe conectarse y hacer el descubrimiento del servicio. Si la respuesta de anuncio / escaneo no proporciona la información, no creo que Android se conecte al dispositivo para obtener el nombre. Al menos, nunca he visto ningún indicio de conexión sin que la aplicación solicite específicamente una conexión. Esto no es lo que desea que se le pida que haga si desea tomar la decisión de conectarse.
Afortunadamente, la mayoría de los dispositivos BTLE con los que he trabajado proporcionan su nombre en el anuncio o en la respuesta de escaneo.
Otra posibilidad es que el dispositivo pueda colocar el nombre en la parte de respuesta de escaneo del anuncio. Dependiendo de cómo se haya configurado el escáner BTLE de Android, uno podría obtener solo los datos de publicidad y no la respuesta de escaneo. En este caso, el nombre no se encontrará si el dispositivo lo coloca en la respuesta de escaneo. Sin embargo, la configuración predeterminada del escáner es tal que se debe recibir una respuesta de escaneo antes de pasar los datos de escaneo a la aplicación.
BluetoothDevice.getName()
puede devolver
null
si no se puede determinar el nombre.
Esto podría deberse a cualquier número de factores.
De todos modos, el nombre es el nombre
descriptivo
del dispositivo y no debe usarse para distinguirlo de otros dispositivos.
En su lugar, use la dirección de hardware a través de
getAddress()
.