Znajdź urządzenia Bluetooth

Interfejs BluetoothAdapter umożliwia znajdowanie zdalnych urządzeń Bluetooth przy użyciu wykrywania urządzeń lub wysyłania zapytań o listę sparowanych urządzeń.

Zanim spróbujesz znaleźć urządzenia Bluetooth, upewnij się, że masz odpowiednie uprawnienia Bluetooth i skonfiguruj Bluetooth w aplikacji.

Wykrywanie urządzeń to procedura skanowania okolicy, która polega na wyszukiwaniu urządzeń z obsługą Bluetooth i wysyłaniu próśb o przesłanie informacji o każdym z nich. Ten proces jest czasami nazywany wykrywaniem, zadawaniem pytań lub skanowaniem. Urządzenie Bluetooth w pobliżu odpowiada na żądanie wykrywania tylko wtedy, gdy w danej chwili przyjmuje je jako wykrywalne. Jeśli urządzenie jest wykrywalne, odpowiada na żądanie wykrywania, udostępniając m.in. nazwę, klasę i unikalny adres MAC urządzenia. Dzięki tym informacjom urządzenie, które przeprowadza proces wykrywania, może zainicjować połączenie z wykrytym urządzeniem.

Urządzenia wykrywalne mogą ujawniać informacje o lokalizacji użytkownika, dlatego proces wykrywania wymaga dostępu do lokalizacji. Jeśli Twoja aplikacja jest używana na urządzeniu z Androidem 8.0 (poziom interfejsu API 26) lub nowszym, rozważ skorzystanie z interfejsu Companion Device Manager API. Ten interfejs API wykrywa urządzenia w imieniu Twojej aplikacji, więc nie musi prosić o dostęp do lokalizacji.

Po pierwszym nawiązaniu połączenia z urządzeniem zdalnym użytkownik automatycznie zobaczy prośbę o sparowanie. Po sparowaniu urządzenia jego podstawowe informacje, takie jak jego nazwa, klasa i adres MAC, są zapisywane i można je odczytać przy użyciu interfejsów API Bluetooth. Przy użyciu znanego adresu MAC urządzenia zdalnego można w każdej chwili nawiązać z nim połączenie bez przeprowadzania wykrywania, o ile urządzenie jest w zasięgu.

Zwróć uwagę, że występuje różnica między parowaniem a łączeniem:

  • Sparowanie oznacza, że 2 urządzenia są świadome swojego istnienia, mają wspólny klucz połączenia, który może służyć do uwierzytelniania, i mogą nawiązywać szyfrowane połączenie.
  • Połączenie oznacza, że urządzenia współdzielą kanał RFCOMM i mogą ze sobą przesyłać dane. Obecne interfejsy API Bluetooth wymagają sparowania urządzeń przed nawiązaniem połączenia RFCOMM. Parowanie jest wykonywane automatycznie po zainicjowaniu zaszyfrowanego połączenia z interfejsami API Bluetooth.

W kolejnych sekcjach opisujemy, jak znaleźć sparowane urządzenia i jak wykrywać nowe urządzenia za pomocą funkcji wykrywania urządzeń.

Wysyłanie zapytań dotyczących sparowanych urządzeń

Przed wykrywaniem urządzeń warto wysłać zapytanie do zestawu sparowanych urządzeń, aby sprawdzić, czy dane urządzenie jest już znane. Aby to zrobić, wywołaj metodę getBondedDevices(). Zwraca zestaw obiektów BluetoothDevice reprezentujących sparowane urządzenia. Możesz na przykład utworzyć zapytanie dotyczące wszystkich sparowanych urządzeń i uzyskać nazwę oraz adres MAC każdego z nich, jak pokazano w tym fragmencie kodu:

Kotlin

val pairedDevices: Set<BluetoothDevice>? = bluetoothAdapter?.bondedDevices
pairedDevices?.forEach { device ->
   val deviceName = device.name
   val deviceHardwareAddress = device.address // MAC address
}

Java

Set<BluetoothDevice> pairedDevices = bluetoothAdapter.getBondedDevices();

if (pairedDevices.size() > 0) {
   // There are paired devices. Get the name and address of each paired device.
   for (BluetoothDevice device : pairedDevices) {
       String deviceName = device.getName();
       String deviceHardwareAddress = device.getAddress(); // MAC address
   }
}

Do zainicjowania połączenia z urządzeniem Bluetooth potrzeba tylko adresu MAC powiązanego z powiązanym obiektem BluetoothDevice. Aby go pobrać, wywołaj metodę getAddress(). Więcej informacji o tworzeniu połączenia znajdziesz w artykule Łączenie urządzeń Bluetooth.

Wykrywanie urządzeń

Aby rozpocząć wykrywanie urządzeń, zadzwoń pod numer startDiscovery(). Proces jest asynchroniczny i zwraca wartość logiczną wskazującą, czy wykrywanie się rozpoczęło. Proces wykrywania zwykle obejmuje ok. 12 sekund skanowania zapytania, po którym następuje skanowanie stron każdego znalezionego urządzenia, aby pobrać jego nazwę Bluetooth.

Aby otrzymywać informacje o każdym wykrytym urządzeniu, aplikacja musi zarejestrować BroadcastReceiver dla intencji ACTION_FOUND. System transmituje tę intencję dla każdego urządzenia. Intencja zawiera dodatkowe pola EXTRA_DEVICE i EXTRA_CLASS, które z kolei zawierają odpowiednio BluetoothDevice i BluetoothClass. Ten fragment kodu pokazuje, jak zarejestrować się, by obsługiwać transmisję po wykryciu urządzeń:

Kotlin

override fun onCreate(savedInstanceState: Bundle?) {
   ...

   // Register for broadcasts when a device is discovered.
   val filter = IntentFilter(BluetoothDevice.ACTION_FOUND)
   registerReceiver(receiver, filter)
}

// Create a BroadcastReceiver for ACTION_FOUND.
private val receiver = object : BroadcastReceiver() {

   override fun onReceive(context: Context, intent: Intent) {
       val action: String = intent.action
       when(action) {
           BluetoothDevice.ACTION_FOUND -> {
               // Discovery has found a device. Get the BluetoothDevice
               // object and its info from the Intent.
               val device: BluetoothDevice =
                       intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)
               val deviceName = device.name
               val deviceHardwareAddress = device.address // MAC address
           }
       }
   }
}

override fun onDestroy() {
   super.onDestroy()
   ...

   // Don't forget to unregister the ACTION_FOUND receiver.
   unregisterReceiver(receiver)
}

Java

@Override
protected void onCreate(Bundle savedInstanceState) {
   ...

   // Register for broadcasts when a device is discovered.
   IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
   registerReceiver(receiver, filter);
}

// Create a BroadcastReceiver for ACTION_FOUND.
private final BroadcastReceiver receiver = new BroadcastReceiver() {
   public void onReceive(Context context, Intent intent) {
       String action = intent.getAction();
       if (BluetoothDevice.ACTION_FOUND.equals(action)) {
           // Discovery has found a device. Get the BluetoothDevice
           // object and its info from the Intent.
           BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
           String deviceName = device.getName();
           String deviceHardwareAddress = device.getAddress(); // MAC address
       }
   }
};

@Override
protected void onDestroy() {
   super.onDestroy();
   ...

   // Don't forget to unregister the ACTION_FOUND receiver.
   unregisterReceiver(receiver);
}

Aby zainicjować połączenie z urządzeniem Bluetooth, wywołaj połączenie getAddress() na BluetoothDevice w celu pobrania powiązanego adresu MAC.

Włącz wykrywalność

Aby urządzenie lokalne było wykrywalne dla innych urządzeń, wywołaj metodę startActivityForResult(Intent, int) z intencją ACTION_REQUEST_DISCOVERABLE. Powoduje to wysłanie żądania włączenia trybu wykrywalnego w systemie bez konieczności otwierania aplikacji Ustawienia, co spowodowałoby zatrzymanie własnej aplikacji. Domyślnie urządzenie jest wykrywalne przez 2 minuty. Możesz ustawić inny czas trwania (maksymalnie 1 godzinę), dodając EXTRA_DISCOVERABLE_DURATION.

Ten fragment kodu ustawia urządzenie jako wykrywalne przez 5 minut:

Kotlin

val requestCode = 1;
val discoverableIntent: Intent = Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE).apply {
   putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300)
}
startActivityForResult(discoverableIntent, requestCode)

Java

int requestCode = 1;
Intent discoverableIntent =
       new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
startActivityForResult(discoverableIntent, requestCode);


Rys. 2. Okno włączania wykrywalności

Pojawi się okno z prośbą o przyznanie użytkownikowi uprawnień, aby umożliwić wykrywanie urządzenia, jak pokazano na rysunku 2. Jeśli użytkownik odpowie „Zezwól”, urządzenie staje się wykrywalne przez określony czas. Aktywność otrzyma wtedy wywołanie wywołania zwrotnego onActivityResult() z kodem wyniku równym czasowi, przez jaki urządzenie jest wykrywalne. Jeśli użytkownik odpowiedział „Odmów” lub wystąpił błąd, kod wyniku to RESULT_CANCELED.

Urządzenie pozostanie w trybie wykrywania bez powiadomienia przez przydzielony czas. Aby otrzymać powiadomienie o zmianie trybu wykrywania, zarejestruj intencję BroadcastReceiver dla intencji ACTION_SCAN_MODE_CHANGED. Ta intencja zawiera dodatkowe pola EXTRA_SCAN_MODE i EXTRA_PREVIOUS_SCAN_MODE, które zapewniają odpowiednio nowy i stary tryb skanowania. Oto możliwe wartości poszczególnych dodatkowych elementów:

SCAN_MODE_CONNECTABLE_DISCOVERABLE
Urządzenie jest w trybie wykrywania.
SCAN_MODE_CONNECTABLE
Urządzenie nie jest w trybie wykrywania, ale nadal może odbierać połączenia.
SCAN_MODE_NONE
Urządzenie nie jest w trybie wykrywania i nie może odbierać połączeń.

Jeśli inicjujesz połączenie z urządzeniem zdalnym, nie musisz włączać możliwości jego wykrycia. Włączenie możliwości wykrywania jest konieczne tylko wtedy, gdy chcesz, aby aplikacja hostowała gniazdo serwera akceptujące połączenia przychodzące, ponieważ przed inicjowaniem połączeń z nimi urządzenia zdalne muszą wykrywać inne urządzenia.