尋找藍牙裝置

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

請先確認您已取得適當的藍牙權限,並設定應用程式的藍牙功能,再嘗試尋找藍牙裝置。

裝置探索是一種掃描程序,可搜尋附近支援藍牙的裝置,並要求取得每部裝置的相關資訊。這項程序有時也稱為探索、查詢或掃描。鄰近的藍牙裝置只有在目前可供偵測時,才會回應探索要求。如果裝置可供偵測,則會分享部分資訊 (例如裝置名稱、類別和專屬 MAC 位址),藉此回應偵測要求。執行探索程序的裝置可利用這項資訊,選擇是否要與所探索的裝置建立連線。

由於可偵測的裝置可能會揭露使用者位置資訊,因此裝置探索程序需要位置資訊存取權。如果應用程式是在搭載 Android 8.0 (API 級別 26) 以上版本的裝置上使用,建議改用Companion Device Manager API。這個 API 會代表您的應用程式執行裝置探索作業,因此應用程式不需要要求位置存取權

首次與遠端裝置建立連線後,系統會自動向使用者顯示配對要求。裝置配對後,系統會儲存該裝置的基本資訊 (例如裝置名稱、類別和 MAC 位址),並可透過藍牙 API 讀取。只要使用遠端裝置的已知 MAC 位址,即可隨時與該裝置建立連線,且無須執行探索作業 (假設裝置仍在範圍內)。

請注意,配對和連線有所不同:

  • 配對的意思是兩部裝置都知道彼此的存在,並且有可用於驗證的共用連結鍵,且能彼此建立加密連線。
  • 「已連線」是指裝置目前共用 RFCOMM 管道,且可相互傳輸資料。目前的藍牙 API 要求裝置必須先配對,才能建立 RFCOMM 連線。您只要透過藍牙 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 額外項目,定義不同的時間長度 (最多五分鐘)。

以下程式碼片段會將裝置設為可在五分鐘內偵測:

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
裝置未處於可偵測模式,因此無法接收連線。

如果您要啟動與遠端裝置的連線,則不需要啟用裝置可偵測性。只有在您希望應用程式主機代管可接受傳入連線的伺服器通訊端時,才需要啟用可探索性,因為遠端裝置必須先探索其他裝置,才能與這些裝置建立連線。