在搭載 Android 8.0 (API 級別 26) 以上版本的裝置上,配對裝置配對功能會代表您的應用程式執行藍牙或 Wi-Fi 掃描鄰近裝置,而不要求 ACCESS_FINE_LOCATION
權限。這有助於盡可能保護使用者隱私。裝置配對完成後,裝置就能使用 REQUEST_COMPANION_RUN_IN_BACKGROUND
和 REQUEST_COMPANION_USE_DATA_IN_BACKGROUND
權限,從背景啟動應用程式。使用這個方法可執行隨附裝置的初始設定,例如支援 BLE 的智慧手錶。此外,必須啟用定位服務,才能配對裝置。
配對裝置配對不會自行建立連線。藍牙和 Wi-Fi 連線 API 建立連線。隨附裝置配對也不會啟用持續掃描功能。
使用者可以從清單中選取裝置,並授予應用程式存取權限。如果您解除安裝應用程式或呼叫 disassociate()
,系統就會撤銷這些權限。如果使用者不再需要自己的關聯,例如登出或移除已綁定的裝置,應用程式必須負責清除自身的關聯。
實作隨附裝置配對連線
如要建立及管理隨附裝置的連線,請使用 CompanionDeviceManager
。本節說明如何透過藍牙、BLE 和 Wi-Fi 將應用程式與隨附裝置配對時,如何自訂配對要求對話方塊。
指定隨附裝置
以下程式碼範例說明如何在資訊清單檔案中加入 <uses-feature>
標記。告知系統您的應用程式想要設定隨附裝置。
<uses-feature android:name="android.software.companion_device_setup"/>
依類型列出裝置
您可以顯示符合所提供篩選器的所有可能隨附裝置,或將顯示範圍限制為單一選項 (如圖 1 所示)。您可以藉由建立篩選器指定應用程式所搜尋的裝置類型,或將 setSingleDevice()
設為 true
(如圖 2 所示)。


如要對要求對話方塊中顯示的隨附裝置清單套用篩選器,請檢查藍牙是否已開啟或檢查 Wi-Fi 是否已開啟。啟用連線後,您可以新增 DeviceFilter
。下列 DeviceFilter
子類別會根據連線類型指定應用程式可建立關聯的裝置類型:
三個子類別都含有可簡化篩選器設定的建構工具。在以下範例中,裝置會掃描具有 BluetoothDeviceFilter
的藍牙裝置。
Kotlin
val deviceFilter: BluetoothDeviceFilter = BluetoothDeviceFilter.Builder() // Match only Bluetooth devices whose name matches the pattern. .setNamePattern(Pattern.compile("My device")) // Match only Bluetooth devices whose service UUID matches this pattern. .addServiceUuid(ParcelUuid(UUID(0x123abcL, -1L)), null) .build()
Java
BluetoothDeviceFilter deviceFilter = new BluetoothDeviceFilter.Builder() // Match only Bluetooth devices whose name matches the pattern. .setNamePattern(Pattern.compile("My device")) // Match only Bluetooth devices whose service UUID matches this pattern. .addServiceUuid(new ParcelUuid(new UUID(0x123abcL, -1L)), null) .build();
將 DeviceFilter
設為 AssociationRequest
,以便裝置管理員判斷要搜尋的裝置類型。
Kotlin
val pairingRequest: AssociationRequest = AssociationRequest.Builder() // Find only devices that match this request filter. .addDeviceFilter(deviceFilter) // Stop scanning as soon as one device matching the filter is found. .setSingleDevice(true) .build()
Java
AssociationRequest pairingRequest = new AssociationRequest.Builder() // Find only devices that match this request filter. .addDeviceFilter(deviceFilter) // Stop scanning as soon as one device matching the filter is found. .setSingleDevice(true) .build();
初始化 AssociationRequest
後,請在 CompanionDeviceManager
上執行 associate()
函式。associate()
函式會接收配對要求物件和回呼。回呼表示應用程式找到裝置,且已準備好啟動對話方塊,供使用者輸入選項時。如果應用程式找不到任何裝置,回呼會傳回錯誤訊息。
在搭載 Android 13 (API 級別 33) 以上版本的裝置上:
Kotlin
val deviceManager = requireContext().getSystemService(Context.COMPANION_DEVICE_SERVICE) val executor: Executor = Executor { it.run() } deviceManager.associate(pairingRequest, executor, object : CompanionDeviceManager.Callback() { // Called when a device is found. Launch the IntentSender so the user // can select the device they want to pair with. override fun onAssociationPending(intentSender: IntentSender) { intentSender?.let { startIntentSenderForResult(it, SELECT_DEVICE_REQUEST_CODE, null, 0, 0, 0) } } override fun onAssociationCreated(associationInfo: AssociationInfo) { // The association is created. } override fun onFailure(errorMessage: CharSequence?) { // Handle the failure. } })
Java
CompanionDeviceManager deviceManager = (CompanionDeviceManager) getSystemService(Context.COMPANION_DEVICE_SERVICE); Executor executor = new Executor() { @Override public void execute(Runnable runnable) { runnable.run(); } }; deviceManager.associate(pairingRequest, new CompanionDeviceManager.Callback() { executor, // Called when a device is found. Launch the IntentSender so the user can // select the device they want to pair with. @Override public void onDeviceFound(IntentSender chooserLauncher) { try { startIntentSenderForResult( chooserLauncher, SELECT_DEVICE_REQUEST_CODE, null, 0, 0, 0 ); } catch (IntentSender.SendIntentException e) { Log.e("MainActivity", "Failed to send intent"); } } @Override public void onAssociationCreated(AssociationInfo associationInfo) { // The association is created. } @Override public void onFailure(CharSequence errorMessage) { // Handle the failure. });
在搭載 Android 12L (API 級別 32) 以下版本 (已淘汰) 的裝置上:
Kotlin
val deviceManager = requireContext().getSystemService(Context.COMPANION_DEVICE_SERVICE) deviceManager.associate(pairingRequest, object : CompanionDeviceManager.Callback() { // Called when a device is found. Launch the IntentSender so the user // can select the device they want to pair with. override fun onDeviceFound(chooserLauncher: IntentSender) { startIntentSenderForResult(chooserLauncher, SELECT_DEVICE_REQUEST_CODE, null, 0, 0, 0) } override fun onFailure(error: CharSequence?) { // Handle the failure. } }, null)
Java
CompanionDeviceManager deviceManager = (CompanionDeviceManager) getSystemService(Context.COMPANION_DEVICE_SERVICE); deviceManager.associate(pairingRequest, new CompanionDeviceManager.Callback() { // Called when a device is found. Launch the IntentSender so the user can // select the device they want to pair with. @Override public void onDeviceFound(IntentSender chooserLauncher) { try { startIntentSenderForResult( chooserLauncher, SELECT_DEVICE_REQUEST_CODE, null, 0, 0, 0 ); } catch (IntentSender.SendIntentException e) { Log.e("MainActivity", "Failed to send intent"); } } @Override public void onFailure(CharSequence error) { // Handle the failure. } }, null);
如要讓使用者選擇要連線的裝置類型,請使用 onAssociationPending()
函式中的 intentSender
參數啟動偏好設定活動。此動作的結果會回傳至偏好設定活動 onActivityResult()
函式中的片段。當使用者根據結果做出選擇時,此更新便會通知您。這樣就能存取所選裝置了。當使用者選取藍牙裝置時,傳送的結果會是 BluetoothDevice
物件。同樣地,當 onAssociationPending()
函式偵測到使用者選取藍牙 LE 裝置時,預期會傳回 android.bluetooth.le.ScanResult
物件。如果是 Wi-Fi 裝置,預期會使用 android.net.wifi.ScanResult
物件。
Kotlin
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { when (requestCode) { SELECT_DEVICE_REQUEST_CODE -> when(resultCode) { Activity.RESULT_OK -> { // The user chose to pair the app with a Bluetooth device. val deviceToPair: BluetoothDevice? = data?.getParcelableExtra(CompanionDeviceManager.EXTRA_DEVICE) deviceToPair?.let { device -> device.createBond() // Continue to interact with the paired device. } } } else -> super.onActivityResult(requestCode, resultCode, data) } }
Java
@Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { if (resultCode != Activity.RESULT_OK) { return; } if (requestCode == SELECT_DEVICE_REQUEST_CODE && data != null) { BluetoothDevice deviceToPair = data.getParcelableExtra(CompanionDeviceManager.EXTRA_DEVICE); if (deviceToPair != null) { deviceToPair.createBond(); // Continue to interact with the paired device. } } else { super.onActivityResult(requestCode, resultCode, data); } }
如要實作隨附裝置配對和篩選器,以便指定裝置並依照類型列出裝置,請參考以下範例:
在搭載 Android 13 (API 級別 33) 以上版本的裝置上:
Kotlin
private const val SELECT_DEVICE_REQUEST_CODE = 0 class MainActivity : AppCompatActivity() { private val deviceManager: CompanionDeviceManager by lazy { getSystemService(Context.COMPANION_DEVICE_SERVICE) as CompanionDeviceManager } val mBluetoothAdapter: BluetoothAdapter by lazy { val java = BluetoothManager::class.java getSystemService(java)!!.adapter } val executor: Executor = Executor { it.run() } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // To skip filters based on names and supported feature flags (UUIDs), // omit calls to setNamePattern() and addServiceUuid() // respectively, as shown in the following Bluetooth example. val deviceFilter: BluetoothDeviceFilter = BluetoothDeviceFilter.Builder() .setNamePattern(Pattern.compile("My device")) .addServiceUuid(ParcelUuid(UUID(0x123abcL, -1L)), null) .build() // The argument provided in setSingleDevice() determines whether a single // device name or a list of them appears. val pairingRequest: AssociationRequest = AssociationRequest.Builder() .addDeviceFilter(deviceFilter) .setSingleDevice(true) .build() // When the app tries to pair with a Bluetooth device, show the // corresponding dialog box to the user. deviceManager.associate(pairingRequest, executor, object : CompanionDeviceManager.Callback() { // Called when a device is found. Launch the IntentSender so the user // can select the device they want to pair with. override fun onAssociationPending(intentSender: IntentSender) { intentSender?.let { startIntentSenderForResult(it, SELECT_DEVICE_REQUEST_CODE, null, 0, 0, 0) } } override fun onAssociationCreated(associationInfo: AssociationInfo) { // AssociationInfo object is created and get association id and the // macAddress. var associationId: int = associationInfo.id var macAddress: MacAddress = associationInfo.deviceMacAddress } override fun onFailure(errorMessage: CharSequence?) { // Handle the failure. } ) override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { when (requestCode) { SELECT_DEVICE_REQUEST_CODE -> when(resultCode) { Activity.RESULT_OK -> { // The user chose to pair the app with a Bluetooth device. val deviceToPair: BluetoothDevice? = data?.getParcelableExtra(CompanionDeviceManager.EXTRA_DEVICE) deviceToPair?.let { device -> device.createBond() // Maintain continuous interaction with a paired device. } } } else -> super.onActivityResult(requestCode, resultCode, data) } } }
Java
class MainActivityJava extends AppCompatActivity { private static final int SELECT_DEVICE_REQUEST_CODE = 0; Executor executor = new Executor() { @Override public void execute(Runnable runnable) { runnable.run(); } }; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); CompanionDeviceManager deviceManager = (CompanionDeviceManager) getSystemService( Context.COMPANION_DEVICE_SERVICE ); // To skip filtering based on name and supported feature flags, // do not include calls to setNamePattern() and addServiceUuid(), // respectively. This example uses Bluetooth. BluetoothDeviceFilter deviceFilter = new BluetoothDeviceFilter.Builder() .setNamePattern(Pattern.compile("My device")) .addServiceUuid( new ParcelUuid(new UUID(0x123abcL, -1L)), null ) .build(); // The argument provided in setSingleDevice() determines whether a single // device name or a list of device names is presented to the user as // pairing options. AssociationRequest pairingRequest = new AssociationRequest.Builder() .addDeviceFilter(deviceFilter) .setSingleDevice(true) .build(); // When the app tries to pair with the Bluetooth device, show the // appropriate pairing request dialog to the user. deviceManager.associate(pairingRequest, new CompanionDeviceManager.Callback() { executor, // Called when a device is found. Launch the IntentSender so the user can // select the device they want to pair with. @Override public void onDeviceFound(IntentSender chooserLauncher) { try { startIntentSenderForResult( chooserLauncher, SELECT_DEVICE_REQUEST_CODE, null, 0, 0, 0 ); } catch (IntentSender.SendIntentException e) { Log.e("MainActivity", "Failed to send intent"); } } @Override public void onAssociationCreated(AssociationInfo associationInfo) { // AssociationInfo object is created and get association id and the // macAddress. int associationId = associationInfo.getId(); MacAddress macAddress = associationInfo.getDeviceMacAddress(); } @Override public void onFailure(CharSequence errorMessage) { // Handle the failure. }); } @Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { if (resultCode != Activity.RESULT_OK) { return; } if (requestCode == SELECT_DEVICE_REQUEST_CODE) { if (resultCode == Activity.RESULT_OK && data != null) { BluetoothDevice deviceToPair = data.getParcelableExtra( CompanionDeviceManager.EXTRA_DEVICE ); if (deviceToPair != null) { deviceToPair.createBond(); // ... Continue interacting with the paired device. } } } else { super.onActivityResult(requestCode, resultCode, data); } } }
在搭載 Android 12L (API 級別 32) 以下版本 (已淘汰) 的裝置上:
Kotlin
private const val SELECT_DEVICE_REQUEST_CODE = 0 class MainActivity : AppCompatActivity() { private val deviceManager: CompanionDeviceManager by lazy { getSystemService(Context.COMPANION_DEVICE_SERVICE) as CompanionDeviceManager } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // To skip filters based on names and supported feature flags (UUIDs), // omit calls to setNamePattern() and addServiceUuid() // respectively, as shown in the following Bluetooth example. val deviceFilter: BluetoothDeviceFilter = BluetoothDeviceFilter.Builder() .setNamePattern(Pattern.compile("My device")) .addServiceUuid(ParcelUuid(UUID(0x123abcL, -1L)), null) .build() // The argument provided in setSingleDevice() determines whether a single // device name or a list of them appears. val pairingRequest: AssociationRequest = AssociationRequest.Builder() .addDeviceFilter(deviceFilter) .setSingleDevice(true) .build() // When the app tries to pair with a Bluetooth device, show the // corresponding dialog box to the user. deviceManager.associate(pairingRequest, object : CompanionDeviceManager.Callback() { override fun onDeviceFound(chooserLauncher: IntentSender) { startIntentSenderForResult(chooserLauncher, SELECT_DEVICE_REQUEST_CODE, null, 0, 0, 0) } override fun onFailure(error: CharSequence?) { // Handle the failure. } }, null) } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { when (requestCode) { SELECT_DEVICE_REQUEST_CODE -> when(resultCode) { Activity.RESULT_OK -> { // The user chose to pair the app with a Bluetooth device. val deviceToPair: BluetoothDevice? = data?.getParcelableExtra(CompanionDeviceManager.EXTRA_DEVICE) deviceToPair?.let { device -> device.createBond() // Maintain continuous interaction with a paired device. } } } else -> super.onActivityResult(requestCode, resultCode, data) } } }
Java
class MainActivityJava extends AppCompatActivity { private static final int SELECT_DEVICE_REQUEST_CODE = 0; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); CompanionDeviceManager deviceManager = (CompanionDeviceManager) getSystemService( Context.COMPANION_DEVICE_SERVICE ); // To skip filtering based on name and supported feature flags, // don't include calls to setNamePattern() and addServiceUuid(), // respectively. This example uses Bluetooth. BluetoothDeviceFilter deviceFilter = new BluetoothDeviceFilter.Builder() .setNamePattern(Pattern.compile("My device")) .addServiceUuid( new ParcelUuid(new UUID(0x123abcL, -1L)), null ) .build(); // The argument provided in setSingleDevice() determines whether a single // device name or a list of device names is presented to the user as // pairing options. AssociationRequest pairingRequest = new AssociationRequest.Builder() .addDeviceFilter(deviceFilter) .setSingleDevice(true) .build(); // When the app tries to pair with the Bluetooth device, show the // appropriate pairing request dialog to the user. deviceManager.associate(pairingRequest, new CompanionDeviceManager.Callback() { @Override public void onDeviceFound(IntentSender chooserLauncher) { try { startIntentSenderForResult(chooserLauncher, SELECT_DEVICE_REQUEST_CODE, null, 0, 0, 0); } catch (IntentSender.SendIntentException e) { // failed to send the intent } } @Override public void onFailure(CharSequence error) { // handle failure to find the companion device } }, null); } @Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { if (requestCode == SELECT_DEVICE_REQUEST_CODE) { if (resultCode == Activity.RESULT_OK && data != null) { BluetoothDevice deviceToPair = data.getParcelableExtra( CompanionDeviceManager.EXTRA_DEVICE ); if (deviceToPair != null) { deviceToPair.createBond(); // ... Continue interacting with the paired device. } } } else { super.onActivityResult(requestCode, resultCode, data); } } }
配對裝置設定檔
Android 12 (API 級別 31) 以上版本的隨附應用程式可在與手錶連線後,使用隨附裝置的設定檔。詳情請參閱「在 Wear OS 上要求權限」指南。
讓隨附應用程式保持喚醒狀態
在 Android 12 (API 級別 31) 以上版本中,您可以使用其他 API,讓隨附應用程式能在隨附裝置在範圍內時保持運作。這些 API 可讓您執行下列操作:
當隨附裝置在範圍內時喚醒應用程式。
詳情請參閱
CompanionDeviceManager.startObservingDevicePresence()
。保證只要隨附裝置保持在範圍內,應用程式程序就會繼續執行。