『轉』android官網翻譯好的藍牙API接口說明


Android 平台包含藍牙網絡堆棧支持,憑借此項支持,設備能以無線方式與其他藍牙設備交換數據。應用框架提供了通過 Android Bluetooth API 訪問藍牙功能的途徑。 這些 API 允許應用以無線方式連接到其他藍牙設備,從而實現點到點和多點無線功能。

使用 Bluetooth API,Android 應用可執行以下操作:

  • 掃描其他藍牙設備
  • 查詢本地藍牙適配器的配對藍牙設備
  • 建立 RFCOMM 通道
  • 通過服務發現連接到其他設備
  • 與其他設備進行雙向數據傳輸
  • 管理多個連接

本文將介紹如何使用傳統藍牙。傳統藍牙適用於電池使用強度較大的操作,例如 Android 設備之間的流式傳輸和通信等。 針對具有低功耗要求的藍牙設備,Android 4.3(API 級別 18)中引入了面向低功耗藍牙的 API 支持。 如需了解更多信息,請參閱低功耗藍牙

基礎知識


本文將介紹如何使用 Android Bluetooth API 來完成使用藍牙進行通信的四項主要任務:設置藍牙、查找局部區域內的配對設備或可用設備、連接設備,以及在設備之間傳輸數據。

android.bluetooth 包中提供了所有 Bluetooth API。 下面概要列出了創建藍牙連接所需的類和接口:

BluetoothAdapter
表示本地藍牙適配器(藍牙無線裝置)。  BluetoothAdapter 是所有藍牙交互的入口點。 利用它可以發現其他藍牙設備,查詢綁定(配對)設備的列表,使用已知的 MAC 地址實例化  BluetoothDevice,以及創建  BluetoothServerSocket 以偵聽來自其他設備的通信。
BluetoothDevice
表示遠程藍牙設備。利用它可以通過  BluetoothSocket 請求與某個遠程設備建立連接,或查詢有關該設備的信息,例如設備的名稱、地址、類和綁定狀態等。
BluetoothSocket
表示藍牙套接字接口(與 TCP  Socket 相似)。這是允許應用通過 InputStream 和 OutputStream 與其他藍牙設備交換數據的連接點。
BluetoothServerSocket
表示用於偵聽傳入請求的開放服務器套接字(類似於 TCP  ServerSocket)。 要連接兩台 Android 設備,其中一台設備必須使用此類開放一個服務器套接字。 當一台遠程藍牙設備向此設備發出連接請求時,  BluetoothServerSocket 將會在接受連接后返回已連接的  BluetoothSocket
BluetoothClass
描述藍牙設備的一般特征和功能。 這是一組只讀屬性,用於定義設備的主要和次要設備類及其服務。 不過,它不能可靠地描述設備支持的所有藍牙配置文件和服務,而是適合作為設備類型提示。
BluetoothProfile
表示藍牙配置文件的接口。  藍牙配置文件是適用於設備間藍牙通信的無線接口規范。 免提配置文件便是一個示例。 如需了解有關配置文件的詳細討論,請參閱 使用配置文件
BluetoothHeadset
提供藍牙耳機支持,以便與手機配合使用。 其中包括藍牙耳機和免提(1.5 版)配置文件。
BluetoothA2dp
定義高質量音頻如何通過藍牙連接和流式傳輸,從一台設備傳輸到另一台設備。“A2DP”代表高級音頻分發配置文件。
BluetoothHealth
表示用於控制藍牙服務的健康設備配置文件代理。
BluetoothHealthCallback
用於實現  BluetoothHealth 回調的抽象類。您必須擴展此類並實現回調方法,以接收關於應用注冊狀態和藍牙通道狀態變化的更新內容。
BluetoothHealthAppConfiguration
表示第三方藍牙健康應用注冊的應用配置,以便與遠程藍牙健康設備通信。
BluetoothProfile.ServiceListener
在  BluetoothProfile IPC 客戶端連接到服務(即,運行特定配置文件的內部服務)或斷開服務連接時向其發送通知的接口。

藍牙權限


要在應用中使用藍牙功能,必須聲明藍牙權限 BLUETOOTH。您需要此權限才能執行任何藍牙通信,例如請求連接、接受連接和傳輸數據等。

如果您希望您的應用啟動設備發現或操作藍牙設置,則還必須聲明 BLUETOOTH_ADMIN 權限。 大多數應用需要此權限僅僅為了能夠發現本地藍牙設備。 除非該應用是將要應用戶請求修改藍牙設置的“超級管理員”,否則不應使用此權限所授予的其他能力。 :如果要使用 BLUETOOTH_ADMIN 權限,則還必須擁有 BLUETOOTH 權限。

在您的應用清單文件中聲明藍牙權限。例如:

<manifest ... >
  <uses-permission android:name="android.permission.BLUETOOTH" />
  ...
</manifest>

如需了解有關聲明應用權限的更多信息,請參閱 <uses-permission> 參考資料。

設置藍牙


圖 1:啟用藍牙對話框。

您需要驗證設備支持藍牙,並且如果支持確保將其啟用,然后您的應用才能通過藍牙進行通信。

如果不支持藍牙,則應正常停用任何藍牙功能。 如果支持藍牙但已停用此功能,則可以請求用戶在不離開應用的同時啟用藍牙。 可使用 BluetoothAdapter,分兩步完成此設置:

  1. 獲取 BluetoothAdapter

    所有藍牙 Activity 都需要 BluetoothAdapter。 要獲取 BluetoothAdapter,請調用靜態 getDefaultAdapter() 方法。這將返回一個表示設備自身的藍牙適配器(藍牙無線裝置)的 BluetoothAdapter。 整個系統有一個藍牙適配器,並且您的應用可使用此對象與之交互。 如果 getDefaultAdapter()返回 null,則該設備不支持藍牙,您的操作到此為止。 例如:

    BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
    if (mBluetoothAdapter == null) {
        // Device does not support Bluetooth
    }
  2. 啟用藍牙

    下一步,您需要確保已啟用藍牙。調用 isEnabled() 以檢查當前是否已啟用藍牙。 如果此方法返回 false,則表示藍牙處於停用狀態。要請求啟用藍牙,請使用 ACTION_REQUEST_ENABLE 操作 Intent 調用 startActivityForResult()。這將通過系統設置發出啟用藍牙的請求(無需停止您的應用)。 例如:

    if (!mBluetoothAdapter.isEnabled()) {
        Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
        startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
    }

    如圖 1 所示,將顯示對話框,請求用戶允許啟用藍牙。如果用戶響應“Yes”,系統將開始啟用藍牙,並在該進程完成(或失敗)后將焦點返回到您的應用。

    傳遞給 startActivityForResult() 的 REQUEST_ENABLE_BT 常量是在局部定義的整型(必須大於 0),系統會將其作為 requestCode 參數傳遞回您的 onActivityResult() 實現。

    如果成功啟用藍牙,您的 Activity 將會在 onActivityResult() 回調中收到 RESULT_OK 結果代碼。 如果由於某個錯誤(或用戶響應“No”)而沒有啟用藍牙,則結果代碼為 RESULT_CANCELED

您的應用還可以選擇偵聽 ACTION_STATE_CHANGED 廣播 Intent,每當藍牙狀態發生變化時,系統都會廣播此 Intent。 此廣播包含額外字段 EXTRA_STATE 和 EXTRA_PREVIOUS_STATE,二者分別包含新的和舊的藍牙狀態。 這些額外字段可能的值包括 STATE_TURNING_ONSTATE_ONSTATE_TURNING_OFF 和 STATE_OFF。偵聽此廣播適用於檢測在您的應用運行期間對藍牙狀態所做的更改。

提示:啟用可檢測性將會自動啟用藍牙。 如果您計划在執行藍牙 Activity 之前一直啟用設備的可檢測性,則可以跳過上述步驟 2。 閱讀下面的啟用可檢測性

查找設備


使用 BluetoothAdapter,您可以通過設備發現或通過查詢配對(綁定)設備的列表來查找遠程藍牙設備。

設備發現是一個掃描過程,它會搜索局部區域內已啟用藍牙功能的設備,然后請求一些關於各台設備的信息(有時也被稱為“發現”、“查詢”或“掃描”)。但局部區域內的藍牙設備僅在其當前已啟用可檢測性時才會響應發現請求。 如果設備可檢測到,它將通過共享一些信息(例如設備名稱、類及其唯一 MAC 地址)來響應發現請求。 利用此信息,執行發現的設備可以選擇發起到被發現設備的連接。

在首次與遠程設備建立連接后,將會自動向用戶顯示配對請求。 設備完成配對后,將會保存關於該設備的基本信息(例如設備名稱、類和 MAC 地址),並且可使用 Bluetooth API 讀取這些信息。 利用遠程設備的已知 MAC 地址可隨時向其發起連接,而無需執行發現操作(假定該設備處於有效范圍內)。

請記住,被配對與被連接之間存在差別。被配對意味着兩台設備知曉彼此的存在,具有可用於身份驗證的共享鏈路密鑰,並且能夠與彼此建立加密連接。 被連接意味着設備當前共享一個 RFCOMM 通道,並且能夠向彼此傳輸數據。 當前的 Android Bluetooth API 要求對設備進行配對,然后才能建立 RFCOMM 連接。 (在使用 Bluetooth API 發起加密連接時,會自動執行配對)。

以下幾節介紹如何查找已配對的設備,或使用設備發現來發現新設備。

:Android 設備默認處於不可檢測到狀態。 用戶可通過系統設置將設備設為在有限的時間內處於可檢測到狀態,或者,應用可請求用戶在不離開應用的同時啟用可檢測性。 下面討論如何啟用可檢測性

查詢配對的設備

在執行設備發現之前,有必要查詢已配對的設備集,以了解所需的設備是否處於已知狀態。 為此,請調用 getBondedDevices()。 這將返回表示已配對設備的一組 BluetoothDevice。 例如,您可以查詢所有已配對設備,然后使用 ArrayAdapter 向用戶顯示每台設備的名稱:

Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();
// If there are paired devices
if (pairedDevices.size() > 0) {
    // Loop through paired devices
    for (BluetoothDevice device : pairedDevices) {
        // Add the name and address to an array adapter to show in a ListView
        mArrayAdapter.add(device.getName() + "\n" + device.getAddress());
    }
}

要發起連接,BluetoothDevice 對象僅僅需要提供 MAC 地址。 在此示例中,它將保存為顯示給用戶的 ArrayAdapter 的一部分。 之后可提取該 MAC 地址,以便發起連接。 您可以在有關連接設備的部分詳細了解如何創建連接。

發現設備

要開始發現設備,只需調用 startDiscovery()。該進程為異步進程,並且該方法會立即返回一個布爾值,指示是否已成功啟動發現操作。 發現進程通常包含約 12 秒鍾的查詢掃描,之后對每台發現的設備進行頁面掃描,以檢索其藍牙名稱。

您的應用必須針對 ACTION_FOUND Intent 注冊一個 BroadcastReceiver,以便接收每台發現的設備的相關信息。 針對每台設備,系統將會廣播ACTION_FOUND Intent。此 Intent 將攜帶額外字段 EXTRA_DEVICE 和 EXTRA_CLASS,二者分別包含 BluetoothDevice 和 BluetoothClass。 例如,下面說明了在發現設備時如何注冊以處理廣播。

// Create a BroadcastReceiver for ACTION_FOUND
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        // When discovery finds a device
        if (BluetoothDevice.ACTION_FOUND.equals(action)) {
            // Get the BluetoothDevice object from the Intent
            BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
            // Add the name and address to an array adapter to show in a ListView
            mArrayAdapter.add(device.getName() + "\n" + device.getAddress());
        }
    }
};
// Register the BroadcastReceiver
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
registerReceiver(mReceiver, filter); // Don't forget to unregister during onDestroy

要發起連接,BluetoothDevice 對象僅僅需要提供 MAC 地址。 在此示例中,它將保存為顯示給用戶的 ArrayAdapter 的一部分。 之后可提取該 MAC 地址,以便發起連接。 您可以在有關連接設備的部分詳細了解如何創建連接。

注意:執行設備發現對於藍牙適配器而言是一個非常繁重的操作過程,並且會消耗大量資源。 在找到要連接的設備后,確保始終使用cancelDiscovery() 停止發現,然后再嘗試連接。 此外,如果您已經保持與某台設備的連接,那么執行發現操作可能會大幅減少可用於該連接的帶寬,因此不應該在處於連接狀態時執行發現操作。

啟用可檢測性

如果您希望將本地設備設為可被其他設備檢測到,請使用 ACTION_REQUEST_DISCOVERABLE 操作 Intent 調用 startActivityForResult(Intent, int)。 這將通過系統設置發出啟用可檢測到模式的請求(無需停止您的應用)。 默認情況下,設備將變為可檢測到並持續 120 秒鍾。 您可以通過添加EXTRA_DISCOVERABLE_DURATION Intent Extra 來定義不同的持續時間。 應用可以設置的最大持續時間為 3600 秒,值為 0 則表示設備始終可檢測到。 任何小於 0 或大於 3600 的值都會自動設為 120 秒。 例如,以下片段會將持續時間設為 300 秒:

Intent discoverableIntent = new
Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
startActivity(discoverableIntent);
圖 2:啟用可檢測性對話框。

如圖 2 所示,將顯示對話框,請求用戶允許將設備設為可檢測到。如果用戶響應“Yes”,則設備將變為可檢測到並持續指定的時間量。 然后,您的 Activity 將會收到對 onActivityResult()) 回調的調用,其結果代碼等於設備可檢測到的持續時間。 如果用戶響應“No”或出現錯誤,結果代碼將為 RESULT_CANCELED

:如果尚未在設備上啟用藍牙,則啟用設備可檢測性將會自動啟用藍牙。

設備將在分配的時間內以靜默方式保持可檢測到模式。如果您希望在可檢測到模式發生變化時收到通知,您可以針對 ACTION_SCAN_MODE_CHANGED Intent 注冊 BroadcastReceiver。 它將包含額外字段 EXTRA_SCAN_MODE 和 EXTRA_PREVIOUS_SCAN_MODE,二者分別告知您新的和舊的掃描模式。 每個字段可能的值包括 SCAN_MODE_CONNECTABLE_DISCOVERABLE、 SCAN_MODE_CONNECTABLE 或 SCAN_MODE_NONE,這些值分別指示設備處於可檢測到模式、未處於可檢測到模式但仍能接收連接,或未處於可檢測到模式並且無法接收連接。

如果您將要發起到遠程設備的連接,則無需啟用設備可檢測性。 僅當您希望您的應用托管將用於接受傳入連接的服務器套接字時,才有必要啟用可檢測性,因為遠程設備必須能夠發現該設備,然后才能發起連接。

連接設備


要在兩台設備上的應用之間創建連接,必須同時實現服務器端和客戶端機制,因為其中一台設備必須開放服務器套接字,而另一台設備必須發起連接(使用服務器設備的 MAC 地址發起連接)。 當服務器和客戶端在同一 RFCOMM 通道上分別擁有已連接的 BluetoothSocket 時,二者將被視為彼此連接。 這種情況下,每台設備都能獲得輸入和輸出流式傳輸,並且可以開始傳輸數據,在有關管理連接的部分將會討論這一主題。 本部分介紹如何在兩台設備之間發起連接。

服務器設備和客戶端設備分別以不同的方法獲得需要的 BluetoothSocket。服務器將在傳入連接被接受時收到套接字。 客戶端將在其打開到服務器的 RFCOMM 通道時收到該套接字。

圖 3:藍牙配對對話框。

一種實現技術是自動將每台設備准備為一個服務器,從而使每台設備開放一個服務器套接字並偵聽連接。然后任一設備可以發起與另一台設備的連接,並成為客戶端。 或者,其中一台設備可顯式“托管”連接並按需開放一個服務器套接字,而另一台設備則直接發起連接。

:如果兩台設備之前尚未配對,則在連接過程中,Android 框架會自動向用戶顯示配對請求通知或對話框(如圖 3 所示)。因此,在嘗試連接設備時,您的應用無需擔心設備是否已配對。 您的 RFCOMM 連接嘗試將被阻塞,直至用戶成功完成配對或配對失敗(包括用戶拒絕配對、配對失敗或超時)。

連接為服務器

當您需要連接兩台設備時,其中一台設備必須通過保持開放的 BluetoothServerSocket 來充當服務器。 服務器套接字的用途是偵聽傳入的連接請求,並在接受一個請求后提供已連接的 BluetoothSocket。 從 BluetoothServerSocket 獲取 BluetoothSocket 后,可以(並且應該)舍棄 BluetoothServerSocket,除非您需要接受更多連接。

以下是設置服務器套接字並接受連接的基本過程:

  1. 通過調用 listenUsingRfcommWithServiceRecord(String, UUID) 獲取 BluetoothServerSocket

    該字符串是您的服務的可識別名稱,系統會自動將其寫入到設備上的新服務發現協議 (SDP) 數據庫條目(可使用任意名稱,也可直接使用您的應用名稱)。 UUID 也包含在 SDP 條目中,並且將作為與客戶端設備的連接協議的基礎。 也就是說,當客戶端嘗試連接此設備時,它會攜帶能夠唯一標識其想要連接的服務的 UUID。 兩個 UUID 必須匹配,在下一步中,連接才會被接受。

  2. 通過調用 accept() 開始偵聽連接請求。

    這是一個阻塞調用。它將在連接被接受或發生異常時返回。 僅當遠程設備發送的連接請求中所包含的 UUID 與向此偵聽服務器套接字注冊的 UUID 相匹配時,連接才會被接受。 操作成功后,accept() 將會返回已連接的 BluetoothSocket

  3. 除非您想要接受更多連接,否則請調用 close()

    這將釋放服務器套接字及其所有資源,但不會關閉 accept() 所返回的已連接的 BluetoothSocket。 與 TCP/IP 不同,RFCOMM 一次只允許每個通道有一個已連接的客戶端,因此大多數情況下,在接受已連接的套接字后立即在 BluetoothServerSocket 上調用 close() 是行得通的。

accept() 調用不應在主 Activity UI 線程中執行,因為它是阻塞調用,並會阻止與應用的任何其他交互。 在您的應用所管理的新線程中使用 BluetoothServerSocket 或 BluetoothSocket 完成所有工作,這通常是一種行之有效的做法。 要終止 accept() 等被阻塞的調用,請通過另一個線程在 BluetoothServerSocket(或 BluetoothSocket)上調用 close(),被阻塞的調用將會立即返回。 請注意,BluetoothServerSocket 或 BluetoothSocket中的所有方法都是線程安全的方法。

示例

以下是一個用於接受傳入連接的服務器組件的簡化線程:

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 = mBluetoothAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID);
        } catch (IOException 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) {
                break;
            }
            // If a connection was accepted
            if (socket != null) {
                // Do work to manage the connection (in a separate thread)
                manageConnectedSocket(socket);
                mmServerSocket.close();
                break;
            }
        }
    }

    /** Will cancel the listening socket, and cause the thread to finish */
    public void cancel() {
        try {
            mmServerSocket.close();
        } catch (IOException e) { }
    }
}

在此示例中,只需要一個傳入連接,因此在接受連接並獲取 BluetoothSocket 之后,應用會立即將獲取的 BluetoothSocket 發送到單獨的線程,關閉BluetoothServerSocket 並中斷循環。

請注意,當 accept() 返回 BluetoothSocket 時,表示套接字已連接好,因此您不應該像在客戶端那樣調用 connect()

manageConnectedSocket() 是應用中的虛構方法,它將啟動用於傳輸數據的線程,在有關管理連接的部分將會討論這一主題。

在完成傳入連接的偵聽后,通常應立即關閉您的 BluetoothServerSocket。 在此示例中,獲取 BluetoothSocket 后立即調用 close()。 您也可能希望在您的線程中提供一個公共方法,以便在需要停止偵聽服務器套接字時關閉私有 BluetoothSocket

連接為客戶端

要發起與遠程設備(保持開放的服務器套接字的設備)的連接,必須首先獲取表示該遠程設備的 BluetoothDevice 對象。(在前面有關查找設備的部分介紹了如何獲取 BluetoothDevice)。 然后必須使用 BluetoothDevice 來獲取 BluetoothSocket 並發起連接。

以下是基本過程:

  1. 使用 BluetoothDevice,通過調用 createRfcommSocketToServiceRecord(UUID) 獲取 BluetoothSocket

    這將初始化將要連接到 BluetoothDevice 的 BluetoothSocket。 此處傳遞的 UUID 必須與服務器設備在使用 listenUsingRfcommWithServiceRecord(String, UUID) 開放其 BluetoothServerSocket 時所用的 UUID 相匹配。 要使用相同的 UUID,只需將該 UUID 字符串以硬編碼方式編入應用,然后通過服務器代碼和客戶端代碼引用該字符串。

  2. 通過調用 connect() 發起連接。

    執行此調用時,系統將會在遠程設備上執行 SDP 查找,以便匹配 UUID。 如果查找成功並且遠程設備接受了該連接,它將共享 RFCOMM 通道以便在連接期間使用,並且 connect() 將會返回。 此方法為阻塞調用。 如果由於任何原因連接失敗或 connect() 方法超時(大約 12 秒之后),它將會引發異常。

    由於 connect() 為阻塞調用,因此該連接過程應始終在主 Activity 線程以外的線程中執行。

    注:在調用 connect() 時,應始終確保設備未在執行設備發現。 如果正在進行發現操作,則會大幅降低連接嘗試的速度,並增加連接失敗的可能性。

示例

以下是發起藍牙連接的線程的基本示例:

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;

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

    public void run() {
        // Cancel discovery because it will slow down the connection
        mBluetoothAdapter.cancelDiscovery();

        try {
            // Connect the device through the socket. This will block
            // until it succeeds or throws an exception
            mmSocket.connect();
        } catch (IOException connectException) {
            // Unable to connect; close the socket and get out
            try {
                mmSocket.close();
            } catch (IOException closeException) { }
            return;
        }

        // Do work to manage the connection (in a separate thread)
        manageConnectedSocket(mmSocket);
    }

    /** Will cancel an in-progress connection, and close the socket */
    public void cancel() {
        try {
            mmSocket.close();
        } catch (IOException e) { }
    }
}

請注意,在建立連接之前會調用 cancelDiscovery()。 在進行連接之前應始終執行此調用,而且調用時無需實際檢查其是否正在運行(但如果您確實想要執行檢查,請調用 isDiscovering())。

manageConnectedSocket() 是應用中的虛構方法,它將啟動用於傳輸數據的線程,在有關管理連接的部分將會討論這一主題。

在完成 BluetoothSocket 后,應始終調用 close() 以執行清理操作。這樣做會立即關閉已連接的套接字並清理所有內部資源。

管理連接


在成功連接兩台(或更多台)設備后,每台設備都會有一個已連接的 BluetoothSocket。 這一點非常有趣,因為這表示您可以在設備之間共享數據。 利用 BluetoothSocket,傳輸任意數據的一般過程非常簡單:

  1. 獲取 InputStream 和 OutputStream,二者分別通過套接字以及 getInputStream() 和 getOutputStream() 來處理數據傳輸。
  2. 使用 read(byte[]) 和 write(byte[]) 讀取數據並寫入到流式傳輸。

就這么簡單。

當然,還需要考慮實現細節。首要的是,應該為所有流式傳輸讀取和寫入操作使用專門的線程。 這一點很重要,因為 read(byte[]) 和 write(byte[]) 方法都是阻塞調用。read(byte[]) 將會阻塞,直至從流式傳輸中讀取內容。write(byte[]) 通常不會阻塞,但如果遠程設備沒有足夠快地調用 read(byte[]),並且中間緩沖區已滿,則其可能會保持阻塞狀態以實現流量控制。因此,線程中的主循環應專門用於讀取 InputStream。 可使用線程中單獨的公共方法來發起對 OutputStream 的寫入操作。

示例

以下是可能的顯示內容示例:

private class ConnectedThread extends Thread {
    private final BluetoothSocket mmSocket;
    private final InputStream mmInStream;
    private final OutputStream mmOutStream;

    public ConnectedThread(BluetoothSocket socket) {
        mmSocket = socket;
        InputStream tmpIn = null;
        OutputStream tmpOut = null;

        // Get the input and output streams, using temp objects because
        // member streams are final
        try {
            tmpIn = socket.getInputStream();
            tmpOut = socket.getOutputStream();
        } catch (IOException e) { }

        mmInStream = tmpIn;
        mmOutStream = tmpOut;
    }

    public void run() {
        byte[] buffer = new byte[1024];  // buffer store for the stream
        int bytes; // bytes returned from read()

        // Keep listening to the InputStream until an exception occurs
        while (true) {
            try {
                // Read from the InputStream
                bytes = mmInStream.read(buffer);
                // Send the obtained bytes to the UI activity
                mHandler.obtainMessage(MESSAGE_READ, bytes, -1, buffer)
                        .sendToTarget();
            } catch (IOException e) {
                break;
            }
        }
    }

    /* Call this from the main activity to send data to the remote device */
    public void write(byte[] bytes) {
        try {
            mmOutStream.write(bytes);
        } catch (IOException e) { }
    }

    /* Call this from the main activity to shutdown the connection */
    public void cancel() {
        try {
            mmSocket.close();
        } catch (IOException e) { }
    }
}

構造函數獲取必要的流式傳輸,並且一旦執行,線程將會等待通過 InputStream 傳入的數據。 當 read(byte[]) 返回流式傳輸中的字節時,將使用來自父類的成員處理程序將數據發送到主 Activity。 然后該方法返回並等待流式傳輸提供更多字節。

發送傳出數據不外乎從主 Activity 調用線程的 write() 方法,並傳入要發送的字節。 然后,此方法直接調用 write(byte[]),將數據發送到遠程設備。

線程的 cancel() 方法很重要,它能通過關閉 BluetoothSocket 隨時終止連接。當您結束藍牙連接的使用時,應始終調用此方法。

有關使用 Bluetooth API 的演示,請參閱藍牙聊天示例應用

使用配置文件


從 Android 3.0 開始,Bluetooth API 便支持使用藍牙配置文件。 藍牙配置文件是適用於設備間藍牙通信的無線接口規范。 免提配置文件便是一個示例。 對於連接到無線耳機的手機,兩台設備都必須支持免提配置文件。

您可以實現接口 BluetoothProfile,通過寫入自己的類來支持特定的藍牙配置文件。 Android Bluetooth API 提供了以下藍牙配置文件的實現:

  • 耳機。耳機配置文件提供了藍牙耳機支持,以便與手機配合使用。 Android 提供了 BluetoothHeadset 類,它是用於通過進程間通信 (IPC) 來控制藍牙耳機服務的代理。 這包括藍牙耳機和免提(1.5 版)配置文件。 BluetoothHeadset 類包含 AT 命令支持。 有關此主題的詳細討論,請參閱供應商特定的 AT 命令
  • A2DP。高級音頻分發配置文件 (A2DP) 定義了高質量音頻如何通過藍牙連接和流式傳輸,從一個設備傳輸到另一個設備。 Android 提供了 BluetoothA2dp 類,它是用於通過 IPC 來控制藍牙 A2DP 服務的代理。
  • 健康設備。Android 4.0(API 級別 14)引入了對藍牙健康設備配置文件 (HDP) 的支持。 這允許您創建應用,使用藍牙與支持藍牙功能的健康設備進行通信,例如心率監測儀、血糖儀、溫度計、台秤等等。 有關支持的設備列表及其相應的設備數據專業化代碼,請參閱 www.bluetooth.org 上的藍牙分配編號。 請注意,這些值在 ISO/IEEE 11073-20601 [7] 規范的“命名法規附錄”中也被稱為 MDC_DEV_SPEC_PROFILE_*。 有關 HDP 的詳細討論,請參閱健康設備配置文件

以下是使用配置文件的基本步驟:

  1. 獲取默認適配器(請參閱設置藍牙)。
  2. 使用 getProfileProxy() 建立到配置文件所關聯的配置文件代理對象的連接。在以下示例中,配置文件代理對象是一個 BluetoothHeadset 的實例。
  3. 設置 BluetoothProfile.ServiceListener。此偵聽程序會在 BluetoothProfile IPC 客戶端連接到服務或斷開服務連接時向其發送通知。
  4. 在 onServiceConnected() 中,獲取配置文件代理對象的句柄。
  5. 獲得配置文件代理對象后,可以立即將其用於監視連接狀態和執行其他與該配置文件相關的操作。

例如,以下代碼片段顯示了如何連接到 BluetoothHeadset 代理對象,以便能夠控制耳機配置文件:

BluetoothHeadset mBluetoothHeadset;

// Get the default adapter
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

// Establish connection to the proxy.
mBluetoothAdapter.getProfileProxy(context, mProfileListener, BluetoothProfile.HEADSET);

private BluetoothProfile.ServiceListener mProfileListener = new BluetoothProfile.ServiceListener() {
    public void onServiceConnected(int profile, BluetoothProfile proxy) {
        if (profile == BluetoothProfile.HEADSET) {
            mBluetoothHeadset = (BluetoothHeadset) proxy;
        }
    }
    public void onServiceDisconnected(int profile) {
        if (profile == BluetoothProfile.HEADSET) {
            mBluetoothHeadset = null;
        }
    }
};

// ... call functions on mBluetoothHeadset

// Close proxy connection after use.
mBluetoothAdapter.closeProfileProxy(mBluetoothHeadset);

供應商特定的 AT 命令

從 Android 3.0 開始,應用可以注冊接收耳機所發送的預定義的供應商特定 AT 命令的系統廣播(例如 Plantronics +XEVENT 命令)。 例如,應用可以接收指示所連接設備的電池電量的廣播,並根據需要通知用戶或采取其他操作。 為 ACTION_VENDOR_SPECIFIC_HEADSET_EVENT intent 創建廣播接收器,以處理耳機的供應商特定 AT 命令。

健康設備配置文件

Android 4.0(API 級別 14)引入了對藍牙健康設備配置文件 (HDP) 的支持。 這允許您創建應用,使用藍牙與支持藍牙功能的健康設備進行通信,例如心率監測儀、血糖儀、溫度計、台秤等等。 Bluetooth Health API 包括類 BluetoothHealthBluetoothHealthCallback 和 BluetoothHealthAppConfiguration,在基礎知識部分介紹了這些類。

在使用 Bluetooth Health API 時,了解以下關鍵 HDP 概念很有幫助:

概念 說明
源設備 在 HDP 中定義的角色。源設備是將醫療數據傳輸到 Android 手機或平板電腦等智能設備的健康設備(體重秤、血糖儀、溫度計等)。
匯集設備 在 HDP 中定義的角色。在 HDP 中,匯集設備是接收醫療數據的智能設備。 在 Android HDP 應用中,匯集設備表示為 BluetoothHealthAppConfiguration 對象。
注冊 指的是注冊特定健康設備的匯集設備。
連接 指的是開放健康設備與 Android 手機或平板電腦等智能設備之間的通道。

創建 HDP 應用

以下是創建 Android HDP 應用所涉及的基本步驟:

  1. 獲取 BluetoothHealth 代理對象的引用。

    與常規耳機和 A2DP 配置文件設備相似,您必須使用 BluetoothProfile.ServiceListener 和 HEALTH 配置文件類型來調用 getProfileProxy(),以便與配置文件代理對象建立連接。

  2. 創建 BluetoothHealthCallback 並注冊充當健康匯集設備的應用配置 (BluetoothHealthAppConfiguration)。
  3. 建立到健康設備的連接。一些設備將會發起該連接。 對於這類設備,無需執行該步驟。
  4. 成功連接到健康設備后,使用文件描述符對健康設備執行讀/寫操作。

    接收的數據需要使用實現了 IEEE 11073-xxxxx 規范的健康管理器進行解釋。

  5. 完成后,關閉健康通道並取消注冊該應用。該通道在長期閑置時也會關閉。

有關描述上述步驟的完整代碼示例,請參閱藍牙 HDP(健康設備配置文件)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM