尋找藍牙裝置

您可以使用 BluetoothAdapter 透過裝置探索或查詢配對裝置清單來尋找遠端藍牙裝置。

嘗試尋找藍牙裝置前,請確認您具備適當的藍牙權限,並為應用程式設定藍牙權限

裝置探索是一項掃描程序,可針對支援藍牙的裝置搜尋本機區域,並要求各裝置的相關資訊。這項程序有時也稱為探索、獲取或掃描。附近的藍牙裝置只有在目前正以可供搜尋的方式接受資訊要求時,才會回應探索要求。如果該裝置可供偵測,就會分享部分資訊 (例如裝置名稱、類別和專屬 MAC 位址),藉此回應探索要求。然後,執行探索程序的裝置即可選擇啟動與發現裝置的連線。

由於可供探索的裝置可能會揭露使用者的位置資訊,因此裝置探索程序需要位置存取權。如果您的應用程式是在搭載 Android 8.0 (API 級別 26) 以上版本的裝置上使用,請考慮改用隨附裝置管理工具 API。這個 API 會代表應用程式執行裝置探索,因此應用程式不需要要求位置存取權

首次與遠端裝置建立連線後,系統會自動向使用者顯示配對要求。配對裝置時,系統會儲存該裝置的基本資訊,例如裝置名稱、類別和 MAC 位址,且可透過 Bluetooth API 讀取。如果使用遠端裝置的已知 MAC 位址,系統隨時都可以透過這部裝置建立連線,不必執行探索 (假設裝置仍在有效範圍內)。

請注意,配對與連線之間有所不同:

  • 「配對」表示兩部裝置知道彼此是否存在,且具備可用來進行驗證的共用連結金鑰,且能夠彼此建立加密連線。
  • 「連線」代表裝置目前共用 RFCOMM 管道,並且能彼此傳輸資料。目前的藍牙 API 要求裝置必須先配對,才能建立 RFCOMM 連線。當您使用 Bluetooth API 啟動加密連線時,系統會自動進行配對。

以下各節將說明如何尋找配對的裝置,以及如何使用裝置探索功能探索新裝置。

查詢配對的裝置

在執行裝置探索作業前,建議您查詢一組配對的裝置,確認是否已知道所需裝置。如要延長,請呼叫 getBondedDevices()。這樣做會回傳代表配對裝置的一組 BluetoothDevice 物件。例如,您可以查詢所有配對的裝置,並取得每部裝置的名稱和 MAC 位址。下列程式碼片段示範:

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

如要與藍牙裝置建立連線,只需從相關聯的 BluetoothDevice 物件取得 MAC 位址即可,呼叫 getAddress() 即可擷取這個位址。如要進一步瞭解如何建立連線,請參閱「連線藍牙裝置」。

探索裝置

如要開始探索裝置,請呼叫 startDiscovery()。這個程序為非同步性質,會傳回布林值,指出探索是否已成功啟動。探索程序通常需要大約 12 秒的調查掃描,接著是頁面掃描找到的每部裝置,以擷取其藍牙名稱。

如要接收找到的每部裝置相關資訊,應用程式必須為 ACTION_FOUND 意圖註冊 BroadcastReceiver。系統會向每部裝置廣播這個意圖。意圖包含額外的欄位 EXTRA_DEVICEEXTRA_CLASS,而這些欄位又包含 BluetoothDeviceBluetoothClass。下列程式碼片段說明如何註冊,以便在找到裝置時處理廣播訊息:

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

如要啟動與藍牙裝置的連線,請在 BluetoothDevice 上呼叫 getAddress() 以擷取相關聯的 MAC 位址。

啟用可偵測性

如要讓其他裝置可以偵測到本機裝置,請使用 ACTION_REQUEST_DISCOVERABLE 意圖呼叫 startActivityForResult(Intent, int)。這會發出要求啟用系統可偵測模式的要求,無須前往「設定」應用程式,這樣應用程式就會停止。根據預設,裝置可供搜尋兩分鐘。您可以新增 EXTRA_DISCOVERABLE_DURATION 額外項目,定義不同的時間長度 (最多一小時)。

下列程式碼片段會將裝置設為 5 分鐘的偵測狀態:

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


圖 2:啟用可偵測性對話方塊。

系統會顯示對話方塊,要求使用者授予權限,讓使用者能偵測裝置,如圖 2 所示。如果使用者回應「允許」,系統就會在指定的時間內偵測到裝置。接著,您的活動會收到對 onActivityResult() 回呼的呼叫,且結果代碼等於裝置可偵測的時間長度。如果使用者回應「拒絕」或發生錯誤,則結果代碼為 RESULT_CANCELED

裝置會在指定時間保持為可偵測模式。如要在可探索模式發生變更時收到通知,請為 ACTION_SCAN_MODE_CHANGED 意圖註冊 BroadcastReceiver。這個意圖包含額外欄位 EXTRA_SCAN_MODEEXTRA_PREVIOUS_SCAN_MODE,分別提供新舊掃描模式。各額外項目的可能值如下:

SCAN_MODE_CONNECTABLE_DISCOVERABLE
裝置處於可供搜尋模式。
SCAN_MODE_CONNECTABLE
裝置未處於可偵測模式,但仍可接收連線。
SCAN_MODE_NONE
裝置未處於可偵測模式,因此無法接收連線。

如要啟動與遠端裝置的連線,不需要啟用裝置可搜尋性,只有在您希望應用程式代管接受傳入連線的伺服器通訊端時,才需要啟用可偵測性,因為遠端裝置必須先能找到其他裝置,才能啟動與其他裝置的連線。