連結藍牙裝置

如要在兩部裝置之間建立連線,必須同時執行 伺服器端和用戶端機制,因為必須在一個裝置開啟伺服器 而另一個必須使用伺服器裝置的 MAC 位址。伺服器裝置和用戶端裝置每個裝置都會取得所需的 BluetoothSocket 是其他地點 管理基礎架構伺服器在連入連線 接受。用戶端在開啟 RFCOMM 管道時,提供通訊端資訊 傳送到伺服器

伺服器和用戶端均相互連線時, 同一個 RFCOMM 管道連結的 BluetoothSocket。到目前為止 裝置可以取得輸入和輸出串流,並開始轉移資料, 與轉移藍牙轉移中 資料。這個區段 說明如何啟動兩部裝置之間的連線。

請確定手邊有合適的 藍牙權限和 請先設定應用程式的藍牙功能。 嘗試尋找藍牙裝置。

連線技術

一種實作技術,是自動將每個裝置準備為伺服器 讓每個裝置都有一個開啟伺服器通訊端並監聽連線。於 遇到這種情況時,任一裝置皆可透過另一部裝置建立連線,成為 用戶端。或者,某部裝置明確代管連線並開啟 伺服器通訊端,另一部裝置會啟動連線。


圖1. 藍牙配對對話方塊。

以伺服器形式連線

如要連接兩部裝置,其中一部裝置必須具有 營業中 BluetoothServerSocket。 伺服器通訊端的用途是監聽傳入連線要求 並在對方接受要求後,提供連結的 BluetoothSocket。當 BluetoothSocket 是從 BluetoothServerSocket 取得, BluetoothServerSocket 可以 (也應該) 遭到捨棄,除非您在 以接受更多連線。

如要設定伺服器通訊端並接受連線,請完成下列步驟 步驟順序:

  1. 致電取得BluetoothServerSocket listenUsingRfcommWithServiceRecord(String, UUID)

    字串是系統可識別的服務名稱 會自動寫入新的服務探索通訊協定 (SDP) 資料庫項目 應用程式。該名稱可任意使用,也可直接輸入您的應用程式名稱。 SDP 項目中也包含通用專屬 ID (UUID) 並建立與用戶端裝置連線協議的基礎。沒錯 是,當用戶端嘗試與這部裝置連線時,會置入 UUID 用於識別要與其連結的服務。這些 UUID 必須相符,系統才能接受連線。

    UUID 是 128 位元的標準化字串 ID,可做為唯一字串 辨識資訊UUID 的用途是找出需要用到的資訊 不重複字元的數量 就是零。需要獨立產生 集中權威力此時,這個欄位會用來識別您的 在 Android 應用程式中使用藍牙服務如要取得與應用程式搭配使用的 UUID,您可以使用 UID 隨機挑選的 UUID 產生器,然後初始化 加上 UUID,其中包含 fromString(String)

  2. 透過呼叫,開始監聽連線要求 accept()

    這是騷擾電話。連線成功後,系統就會傳回這個錯誤 或是發生例外狀況時只有在 遠端裝置傳送了連線要求,其中包含相符的 UUID 與此監聽伺服器通訊端註冊的 Pod如果成功的話 accept() 會傳回已連結的 BluetoothSocket

  3. 除非您想接受其他連線,否則請致電 close()

    這個方法呼叫會釋出伺服器通訊端及其所有資源, 不會關閉由開發人員傳回的已連結 BluetoothSocket accept()。有別於 TCP/IP,RFCOMM 僅允許每個 因此在多數情況下,呼叫 close() 是合理的 接受連線的通訊端後立即 BluetoothServerSocket

由於 accept() 呼叫是造成阻斷的呼叫,請勿在主程式中執行 活動 UI 執行緒。在其他執行緒中執行,可確保應用程式 仍會回應其他使用者的互動情形。通常能處理所有工作 與新執行緒中的 BluetoothServerSocketBluetoothSocket 相關 是由您的應用程式管理如要取消已封鎖的通話 (例如 accept()),請呼叫 close() 其他執行緒上的 BluetoothServerSocketBluetoothSocket。注意事項 BluetoothServerSocketBluetoothSocket 上的所有方法 執行緒安全。

範例

以下是接受的伺服器元件的簡化執行緒 連入連線:

Kotlin

private inner class AcceptThread : Thread() {

   private val mmServerSocket: BluetoothServerSocket? by lazy(LazyThreadSafetyMode.NONE) {
       bluetoothAdapter?.listenUsingInsecureRfcommWithServiceRecord(NAME, MY_UUID)
   }

   override fun run() {
       // Keep listening until exception occurs or a socket is returned.
       var shouldLoop = true
       while (shouldLoop) {
           val socket: BluetoothSocket? = try {
               mmServerSocket?.accept()
           } catch (e: IOException) {
               Log.e(TAG, "Socket's accept() method failed", e)
               shouldLoop = false
               null
           }
           socket?.also {
               manageMyConnectedSocket(it)
               mmServerSocket?.close()
               shouldLoop = false
           }
       }
   }

   // Closes the connect socket and causes the thread to finish.
   fun cancel() {
       try {
           mmServerSocket?.close()
       } catch (e: IOException) {
           Log.e(TAG, "Could not close the connect socket", e)
       }
   }
}

Java

private class AcceptThread extends Thread {
   private final BluetoothServerSocket mmServerSocket;

   public AcceptThread() {
       // Use a temporary object that is later assigned to mmServerSocket
       // because mmServerSocket is final.
       BluetoothServerSocket tmp = null;
       try {
           // MY_UUID is the app's UUID string, also used by the client code.
           tmp = bluetoothAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID);
       } catch (IOException e) {
           Log.e(TAG, "Socket's listen() method failed", e);
       }
       mmServerSocket = tmp;
   }

   public void run() {
       BluetoothSocket socket = null;
       // Keep listening until exception occurs or a socket is returned.
       while (true) {
           try {
               socket = mmServerSocket.accept();
           } catch (IOException e) {
               Log.e(TAG, "Socket's accept() method failed", e);
               break;
           }

           if (socket != null) {
               // A connection was accepted. Perform work associated with
               // the connection in a separate thread.
               manageMyConnectedSocket(socket);
               mmServerSocket.close();
               break;
           }
       }
   }

   // Closes the connect socket and causes the thread to finish.
   public void cancel() {
       try {
           mmServerSocket.close();
       } catch (IOException e) {
           Log.e(TAG, "Could not close the connect socket", e);
       }
   }
}

本例中只需要一個連入連線,因此 系統接受連線並取得 BluetoothSocket,應用程式就會傳遞 將 BluetoothSocket 取得到另一個執行緒,這時會關閉 BluetoothServerSocket,並在迴圈中中斷。

請注意,當 accept() 傳回 BluetoothSocket 時,通訊端已經 已連線。因此,您不應呼叫 connect(),就跟您一樣 執行這些指令

應用程式專屬的 manageMyConnectedSocket() 方法的用意是啟動 移轉資料的執行緒,詳情請參閱相關主題 傳輸藍牙 資料

一般而言,你應該會在完成後關閉 BluetoothServerSocket 監聽傳入連線在這個範例中,close() 會在呼叫後立即呼叫 才會因為取得 BluetoothSocket 而改變。您也可以將 可在事件中關閉私人 BluetoothSocket 的方法 不需要停止監聽伺服器通訊端

以用戶端身分連線

為了與接受的遠端裝置建立連線 在開放伺服器通訊端上連線時,您必須先取得 BluetoothDevice 物件。如要瞭解 BluetoothDevice,請參閱「尋找藍牙」 裝置。您必須 然後使用 BluetoothDevice 取得 BluetoothSocket,並啟動 以獲得最佳效能和最安全的連線

基本流程如下:

  1. 使用 BluetoothDevice 取得 BluetoothSocket,方法是呼叫 createRfcommSocketToServiceRecord(UUID)

    這個方法會初始化 BluetoothSocket 物件,讓用戶端 連線至 BluetoothDevice。在此傳遞的 UUID 必須與使用的 UUID 相符 一律會由伺服器裝置發出呼叫 listenUsingRfcommWithServiceRecord(String, UUID)敬上 開啟它的 BluetoothServerSocket。如要使用相符的 UUID,請將 傳入應用程式中的 UUID 字串,然後從兩個伺服器參照該字串 和用戶端程式碼

  2. 呼叫 connect() 以啟動連線。請注意,這個方法 。

    用戶端呼叫此方法後,系統會執行 SDP 查詢來尋找 產生相符 UUID。如果查詢成功,且 遠端裝置接受連線後,就會共用要使用的 RFCOMM 頻道 連線期間,connect() 方法就會傳回。如果連線 如果 connect() 方法逾時 (約 12 秒之後),則 此方法會擲回 IOException

由於 connect() 是阻斷呼叫,請一律執行此操作 在與主要活動 (UI) 不同的執行緒中連線程序 。

範例

以下是用戶端執行緒啟動藍牙的基本範例 連線:

Kotlin

private inner class ConnectThread(device: BluetoothDevice) : Thread() {

   private val mmSocket: BluetoothSocket? by lazy(LazyThreadSafetyMode.NONE) {
       device.createRfcommSocketToServiceRecord(MY_UUID)
   }

   public override fun run() {
       // Cancel discovery because it otherwise slows down the connection.
       bluetoothAdapter?.cancelDiscovery()

       mmSocket?.let { socket ->
           // Connect to the remote device through the socket. This call blocks
           // until it succeeds or throws an exception.
           socket.connect()

           // The connection attempt succeeded. Perform work associated with
           // the connection in a separate thread.
           manageMyConnectedSocket(socket)
       }
   }

   // Closes the client socket and causes the thread to finish.
   fun cancel() {
       try {
           mmSocket?.close()
       } catch (e: IOException) {
           Log.e(TAG, "Could not close the client socket", e)
       }
   }
}

Java

private class ConnectThread extends Thread {
   private final BluetoothSocket mmSocket;
   private final BluetoothDevice mmDevice;

   public ConnectThread(BluetoothDevice device) {
       // Use a temporary object that is later assigned to mmSocket
       // because mmSocket is final.
       BluetoothSocket tmp = null;
       mmDevice = device;

       try {
           // Get a BluetoothSocket to connect with the given BluetoothDevice.
           // MY_UUID is the app's UUID string, also used in the server code.
           tmp = device.createRfcommSocketToServiceRecord(MY_UUID);
       } catch (IOException e) {
           Log.e(TAG, "Socket's create() method failed", e);
       }
       mmSocket = tmp;
   }

   public void run() {
       // Cancel discovery because it otherwise slows down the connection.
       bluetoothAdapter.cancelDiscovery();

       try {
           // Connect to the remote device through the socket. This call blocks
           // until it succeeds or throws an exception.
           mmSocket.connect();
       } catch (IOException connectException) {
           // Unable to connect; close the socket and return.
           try {
               mmSocket.close();
           } catch (IOException closeException) {
               Log.e(TAG, "Could not close the client socket", closeException);
           }
           return;
       }

       // The connection attempt succeeded. Perform work associated with
       // the connection in a separate thread.
       manageMyConnectedSocket(mmSocket);
   }

   // Closes the client socket and causes the thread to finish.
   public void cancel() {
       try {
           mmSocket.close();
       } catch (IOException e) {
           Log.e(TAG, "Could not close the client socket", e);
       }
   }
}

請注意,在這個程式碼片段中,系統會在連線前呼叫 cancelDiscovery() 。您應一律在 connect() 之前呼叫 cancelDiscovery()。 尤其是 cancelDiscovery() 成功與否,無論裝置為何 探索作業目前正在進行中。如果應用程式需要判斷 正在搜尋裝置,您可以使用 isDiscovering()

應用程式專屬的 manageMyConnectedSocket() 方法的用意是啟動 轉移資料的執行緒,詳情請參閱 傳輸藍牙資料

使用完 BluetoothSocket 後,請一律呼叫 close()。建議做法 會立即關閉連線的通訊端並釋放所有相關內部 再複習一下,機構節點 是所有 Google Cloud Platform 資源的根節點