Tìm thiết bị Bluetooth

Bằng cách sử dụng BluetoothAdapter, bạn có thể tìm thấy các thiết bị Bluetooth từ xa thông qua tính năng khám phá thiết bị hoặc bằng cách truy vấn danh sách các thiết bị đã ghép nối.

Hãy đảm bảo bạn có quyền truy cập Bluetooth thích hợp và thiết lập ứng dụng của mình để sử dụng Bluetooth trước khi thử tìm thiết bị Bluetooth.

Khám phá thiết bị là quy trình quét tìm các thiết bị có hỗ trợ Bluetooth trong khu vực cục bộ và yêu cầu một số thông tin về từng thiết bị. Quá trình này đôi khi được gọi là phát hiện, yêu cầu hoặc quét. Một thiết bị Bluetooth ở gần chỉ phản hồi yêu cầu khám phá nếu thiết bị đó hiện chấp nhận các yêu cầu thông tin bằng cách có thể phát hiện. Nếu có thể phát hiện được một thiết bị, thiết bị đó sẽ phản hồi yêu cầu khám phá bằng cách chia sẻ một số thông tin, chẳng hạn như tên, lớp và địa chỉ MAC duy nhất của thiết bị. Khi sử dụng thông tin này, thiết bị đang thực hiện quá trình khám phá có thể chọn bắt đầu kết nối với thiết bị được phát hiện.

Vì các thiết bị có thể phát hiện có thể tiết lộ thông tin về vị trí của người dùng, nên quá trình khám phá thiết bị yêu cầu quyền truy cập thông tin vị trí. Nếu ứng dụng của bạn đang được dùng trên một thiết bị chạy Android 8.0 (API cấp 26) trở lên, hãy cân nhắc sử dụng API Trình quản lý thiết bị đồng hành. API này thay mặt ứng dụng của bạn thực hiện quá trình khám phá thiết bị, vì vậy, ứng dụng của bạn không cần yêu cầu quyền truy cập thông tin vị trí.

Sau lần đầu kết nối bằng thiết bị từ xa, yêu cầu ghép nối sẽ tự động hiển thị với người dùng. Khi một thiết bị được ghép nối, thông tin cơ bản về thiết bị đó (chẳng hạn như tên, lớp và địa chỉ MAC của thiết bị) sẽ được lưu lại và có thể được đọc bằng các API Bluetooth. Khi sử dụng địa chỉ MAC đã biết cho một thiết bị từ xa, bạn có thể bắt đầu kết nối với thiết bị đó bất cứ lúc nào mà không cần phải tìm ra, giả sử thiết bị vẫn nằm trong phạm vi.

Lưu ý rằng có sự khác biệt giữa việc được ghép nối và được kết nối:

  • Được ghép nối có nghĩa là hai thiết bị biết được sự tồn tại của nhau, có một khoá liên kết dùng chung có thể dùng để xác thực và có thể thiết lập kết nối được mã hoá với nhau.
  • Được kết nối có nghĩa là các thiết bị hiện dùng chung một kênh RFCOMM và có thể truyền dữ liệu với nhau. Các API Bluetooth hiện tại yêu cầu ghép nối thiết bị trước khi có thể thiết lập kết nối RFCOMM. Quá trình ghép nối sẽ tự động được thực hiện khi bạn bắt đầu kết nối đã mã hoá bằng API Bluetooth.

Các phần sau đây mô tả cách tìm thiết bị đã được ghép nối và cách khám phá thiết bị mới bằng tính năng khám phá thiết bị.

Truy vấn thiết bị đã ghép nối

Trước khi thực hiện quá trình khám phá thiết bị, bạn nên truy vấn nhóm thiết bị đã ghép nối để xem thiết bị mong muốn đã được xác định hay chưa. Để thực hiện việc này, hãy gọi getBondedDevices(). Thao tác này sẽ trả về một tập hợp các đối tượng BluetoothDevice đại diện cho các thiết bị đã ghép nối. Ví dụ: bạn có thể truy vấn tất cả các thiết bị đã ghép nối rồi nhận tên cũng như địa chỉ MAC của từng thiết bị, như đoạn mã sau đây minh hoạ:

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

Để bắt đầu kết nối với một thiết bị Bluetooth, tất cả những gì cần thiết từ đối tượng BluetoothDevice được liên kết là địa chỉ MAC mà bạn truy xuất bằng cách gọi getAddress(). Bạn có thể tìm hiểu thêm về cách tạo kết nối trong bài viết Kết nối thiết bị Bluetooth.

Khám phá thiết bị

Để bắt đầu khám phá thiết bị, hãy gọi startDiscovery(). Quá trình này không đồng bộ và trả về một giá trị boolean cho biết quá trình khám phá đã bắt đầu thành công hay chưa. Quá trình khám phá thường bao gồm một quá trình quét truy vấn trong khoảng 12 giây, sau đó là quét trang của từng thiết bị được tìm thấy để truy xuất tên Bluetooth của thiết bị đó.

Để nhận thông tin về từng thiết bị đã phát hiện, ứng dụng của bạn phải đăng ký BroadcastReceiver cho ý định ACTION_FOUND. Hệ thống sẽ truyền ý định này cho từng thiết bị. Ý định này chứa các trường bổ sung EXTRA_DEVICEEXTRA_CLASS, lần lượt chứa BluetoothDeviceBluetoothClass. Đoạn mã sau đây cho biết cách bạn có thể đăng ký để xử lý thông báo truyền tin khi tìm thấy thiết bị:

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

Để bắt đầu kết nối với thiết bị Bluetooth, bạn hãy gọi getAddress() trên BluetoothDevice để truy xuất địa chỉ MAC được liên kết.

Bật khả năng phát hiện

Để các thiết bị khác có thể tìm thấy thiết bị cục bộ này, hãy gọi startActivityForResult(Intent, int) với ý định ACTION_REQUEST_DISCOVERABLE. Việc này sẽ đưa ra yêu cầu bật chế độ phát hiện của hệ thống mà không cần phải chuyển đến ứng dụng Cài đặt. Thao tác này sẽ dừng ứng dụng của bạn. Theo mặc định, thiết bị sẽ ở chế độ phát hiện được trong 2 phút. Bạn có thể xác định một thời lượng khác, tối đa một giờ, bằng cách thêm thông số bổ sung EXTRA_DISCOVERABLE_DURATION.

Đoạn mã sau đây đặt thiết bị ở chế độ có thể phát hiện được trong 5 phút:

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


Hình 2: Hộp thoại bật khả năng phát hiện.

Một hộp thoại sẽ xuất hiện, yêu cầu người dùng cấp quyền để thiết bị đó có thể phát hiện được, như minh hoạ trong hình 2. Nếu người dùng phản hồi "Cho phép" thì thiết bị sẽ có thể phát hiện được trong khoảng thời gian đã chỉ định. Sau đó, hoạt động của bạn sẽ nhận được một lệnh gọi đến lệnh gọi lại onActivityResult(), với mã kết quả bằng với thời lượng mà thiết bị có thể phát hiện được. Nếu người dùng phản hồi "Từ chối" hoặc nếu xảy ra lỗi, mã kết quả sẽ là RESULT_CANCELED.

Thiết bị sẽ tự động ở chế độ có thể phát hiện trong thời gian quy định. Để được thông báo khi chế độ phát hiện đã thay đổi, hãy đăng ký BroadcastReceiver cho ý định ACTION_SCAN_MODE_CHANGED. Ý định này chứa các trường bổ sung EXTRA_SCAN_MODEEXTRA_PREVIOUS_SCAN_MODE cung cấp chế độ quét mới và cũ tương ứng. Có thể có giá trị cho từng dữ liệu bổ sung như sau:

SCAN_MODE_CONNECTABLE_DISCOVERABLE
Thiết bị đang ở chế độ phát hiện.
SCAN_MODE_CONNECTABLE
Thiết bị không ở chế độ phát hiện nhưng vẫn có thể nhận kết nối.
SCAN_MODE_NONE
Thiết bị không ở chế độ phát hiện và không thể nhận kết nối.

Nếu đang khởi tạo kết nối với một thiết bị từ xa, bạn không cần bật khả năng phát hiện thiết bị. Bạn chỉ cần bật khả năng phát hiện khi muốn ứng dụng của mình lưu trữ một ổ cắm máy chủ chấp nhận các kết nối đến, vì các thiết bị từ xa phải có khả năng phát hiện các thiết bị khác trước khi bắt đầu kết nối với những thiết bị khác đó.