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);
});
最後に、私は解決策を見つけました:
1.接続されているデバイスの場合:
Gatt特性からデバイス名を読み取ります org.bluetooth.characteristic.gap.device_name of service org.bluetooth.service.generic_access 。
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
を返す場合があります。これは、さまざまな要因が原因である可能性があります。とにかく、名前はfriendlyデバイスの名前であり、他のデバイスと区別するために使用されるべきではありません。代わりに、getAddress()
を介してハードウェアアドレスを使用してください。
私はこれが古いことを知っていますが、このより仕様指向の答えはいくつかのケースに答えるのに役立つかもしれません。
Bluetooth Low Energyでは、アドバタイズとスキャン応答データはBluetoothアドレスを持つ必要があるだけです。アドバタイズメントデータは、クライアントBTLEエンドポイントがサービスデバイスを検出する方法です。クライアントはスキャン応答を要求して、より多くのデータを取得できます。このデータでは、デバイス名はオプションです。ただし、BTLE仕様では、すべてのBluetooth LowEnergyエンドポイントがデバイス名特性をサポートするために必要な汎用アクセスサービスをサポートする必要があります。残念ながら、その特性を読み取るには、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();
}
RN4020 Bluetoothモジュールの名前を表示しようとしましたが、同じ問題が発生しました。 Microchipのフォーラムで問題を見つけました:
プライベートサービスまたはMLDPを有効にした場合、31バイトのアドバタイズメントペイロードの制限により、デバイス名の最大バイトは6バイトです。
デバイス名を9文字に設定しました。名前を4バイトに設定すると、問題が修正されました。
カスタムサービスのUUIDを認識してデバイスを認識している場合は、デバイスに接続してその名前を読み取ることもできます(私の場合は6バイトより長い場合)。これはMicrochipsフォーラムでも提案されました。
スキャン時にデバイスが検出された直後にデバイスの名前を照会すると、nullが返される場合があることがわかりました。これを回避するために、UIスレッドでランナブルを1秒ごとに最大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);
}
}