1. 程式人生 > >藍芽開發--Google文件(譯) (文章末有完整專案連結)

藍芽開發--Google文件(譯) (文章末有完整專案連結)

藍芽

Android平臺包括對藍芽網路堆疊的支援,允許裝置與其他藍芽裝置進行無線交換資料。應用程式框架通過Android藍芽API提供對藍芽功能的訪問。這些API讓應用程式無線連線到其他藍芽裝置,實現點對點和多點無線功能。

使用藍芽API,Android應用程式可以執行以下操作:

  • 掃描其他藍芽裝置
  • 查詢配對藍芽裝置的本地藍芽介面卡
  • 建立RFCOMM通道
  • 通過服務發現連線到其他裝置
  • 向其他裝置傳輸資料
  • 管理多個連線

本文件介紹如何使用Classic藍芽。經典藍芽是更多電池密集型操作(如Android裝置之間的流媒體和通訊)的理想選擇。對於低功耗要求的藍芽裝置,Android 4.3(API Level 18)為藍芽低功耗引入了API支援。要了解更多資訊,請參閱

藍芽低功耗

基礎

本文件介紹如何使用Android藍芽API完成使用藍芽進行通訊所需的四個主要任務:設定藍芽,查詢本地配對或可用的裝置,連線裝置以及在裝置之間傳輸資料。

所有的藍芽API都可以在android.bluetooth包中使用。以下是建立藍芽連線所需的類和介面的總結:

 BluetoothAdapter

表示本地藍芽介面卡(藍芽無線電)。這 BluetoothAdapter是所有藍芽互動的入門點。使用此功能,您可以發現其他藍芽裝置,查詢已繫結(配對)裝置的列表,BluetoothDevice使用已知的MAC地址例項化,並建立一個BluetoothServerSocket監聽來自其他裝置的通訊。

BluetoothDevice

表示遠端藍芽裝置。使用此方法通過BluetoothSocket關於裝置的或查詢資訊(如其名稱,地址,類別和繫結狀態)來請求與遠端裝置的連線。

BluetoothSocket

表示藍芽插座的介面(類似於TCP Socket)。這是允許應用程式通過InputStream和OutputStream與另一個藍芽裝置交換資料的連線點。

BluetoothServerSocket

表示用於偵聽傳入請求(類似於TCP ServerSocket)的開啟的伺服器套接字。為了連線兩個Android裝置,一個裝置必須開啟這個類的伺服器套接字。當遠端藍芽裝置向該裝置發出連線請求時,當接受BluetoothServerSocket連線BluetoothSocket時, 將返回連線。

BluetoothClass
描述藍芽裝置的一般特性和功能。這是一組只讀屬性,用於定義裝置的主要和次要裝置類及其服務。但是,這不能可靠地描述裝置支援的所有藍芽配置檔案和服務,但對裝置型別的提示很有用。
BluetoothProfile
表示藍芽配置檔案的介面。甲藍芽配置檔案是用於在裝置之間基於藍芽的通訊的無線介面規範。一個例子是擴音配置檔案。有關配置檔案的更多討論,請參閱使用配置檔案
BluetoothHeadset
支援藍芽耳機與手機配合使用。這包括藍芽耳機和擴音(v1.5)配置檔案。
BluetoothA2dp
定義通過藍芽連線將高質量的音訊流從一個裝置傳輸到另一個裝置。“A2DP”表示高階音訊分配配置檔案。
BluetoothHealth
表示控制藍芽服務的執行狀況裝置配置檔案代理。
BluetoothHealthCallback
用於實現BluetoothHealth回撥的抽象類。您必須擴充套件此類並實現回撥方法以接收有關應用程式註冊狀態和藍芽通道狀態更改的更新。
BluetoothHealthAppConfiguration
表示藍芽健康第三方應用程式註冊以與遠端藍芽健康裝置進行通訊的應用程式配置。
BluetoothProfile.ServiceListener
BluetoothProfile當IPC客戶端連線到伺服器或與服務斷開連線(即執行特定配置檔案的內部伺服器)時,可以通知IPC客戶端。

藍芽許可權

為了在您的應用程式中使用藍芽功能,您必須宣告藍芽許可權BLUETOOTH。您需要此許可權才能執行任何藍芽通訊,例如請求連線,接受連線和傳輸資料。

如果您希望您的應用啟動裝置發現或操縱藍芽設定,您還必須宣告BLUETOOTH_ADMIN 許可權。大多數應用程式僅需要此許可權才能發現本地藍芽裝置。除非應用程式是根據使用者請求修改藍芽設定的“電源管理器”,否則不得使用此許可權授予的其他功能。注意:如果您使用BLUETOOTH_ADMIN許可權,那麼您還必須具有該BLUETOOTH許可權。

宣告您的應用程式清單檔案中的藍芽許可權。例如:

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

設定藍芽

在您的應用程式可以通過藍芽進行通訊之前,您需要驗證裝置是否支援藍芽,如果是,請確保已啟用藍芽。

如果不支援藍芽,則應優雅地禁用任何藍芽功能。如果支援藍芽但禁用藍芽,則可以要求使用者在不離開應用程式的情況下啟用藍芽。這個設定是通過兩個步驟完成的BluetoothAdapter。

  1. 得到 BluetoothAdapter
    將BluetoothAdapter所需的任何和所有的藍芽活動。要獲取BluetoothAdapter,請呼叫靜態getDefaultAdapter()方法。這將返回一個 BluetoothAdapter表示裝置自己的藍芽介面卡(藍芽無線電)的。整個系統有一個藍芽介面卡,您的應用程式可以使用此物件與其進行互動。如果 getDefaultAdapter()返回null,則裝置不支援藍芽,您的故事將在此結束。例如:
//獲取藍芽介面卡
        bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        if (bluetoothAdapter != null) {
            Toast.makeText(this, "支援藍芽裝置!", Toast.LENGTH_SHORT).show();
        }
  1. 啟用藍芽
    接下來,您需要確保啟用藍芽。呼叫isEnabled()檢查藍芽是否當前啟用。如果此方法返回false,則藍芽被禁用。要請求啟用藍芽,請startActivityForResult() 使用ACTION_REQUEST_ENABLEIntent操作呼叫。這將發出通過系統設定啟用藍芽的請求(不停止您的應用程式)。例如:
if (!mBluetoothAdapter.isEnabled()) {
    Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
    startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}

將顯示一個對話方塊,要求使用者啟用藍芽功能,如圖1所示。如果使用者響應“是”,系統將開始啟用藍芽,一旦程序完成(或失敗),系統將重新啟動應用程式。
REQUEST_ENABLE_BT傳遞給的常數startActivityForResult()是一個本地定義的整數(必須大於0),系統將在onActivityResult()實現中作為 requestCode引數傳回給您 。
如果啟用藍芽成功,您的活動將RESULT_OK在onActivityResult() 回撥中接收結果程式碼。如果由於錯誤(或使用者響應“否”)未啟用藍芽,則結果程式碼為RESULT_CANCELED。
可選地,您的應用程式還可以監聽 ACTION_STATE_CHANGED廣播Intent,當Bluetooth狀態發生變化時,系統將廣播Int Int。該廣播包含額外的欄位EXTRA_STATE和EXTRA_PREVIOUS_STATE,包含新老的藍芽狀態,分別。這些額外的欄位可能的值是 STATE_TURNING_ON,STATE_ON,STATE_TURNING_OFF,和STATE_OFF。聆聽此廣播可能會有助於在您的應用程式執行時檢測對藍芽狀態所做的更改。

提示:啟用可發現性將自動啟用藍芽。如果您打算在執行藍芽活動之前始終啟用裝置可發現性,則可以跳過上述步驟2。

查詢裝置

使用該功能BluetoothAdapter,您可以通過裝置發現或通過查詢配對(繫結)裝置列表來查詢遠端藍芽裝置。

裝置發現是一種掃描過程,可以在本地搜尋藍芽裝置,然後請求一些關於每個裝置的資訊(這有時被稱為“發現”,“查詢”或“掃描”)。但是,本地區內的藍芽裝置只有在當前啟用才能發現的情況下才能響應發現請求。如果裝置是可發現的,它將通過共享一些資訊來響應發現請求,例如裝置名稱,類別及其唯一的MAC地址。使用此資訊,執行發現的裝置隨後可以選擇啟動與發現的裝置的連線。

一旦與第一次使用遠端裝置進行連線,配對請求將自動呈現給使用者。當裝置配對時,將儲存有關該裝置的基本資訊(如裝置名稱,類和MAC地址),並使用藍芽API進行讀取。使用已知的MAC地址進行遠端裝置,可以在任何時間啟動連線,而無需執行發現(假定裝置在範圍內)。

記住配對和連線之間有區別。要配對意味著兩個裝置都知道彼此的存在,具有可以用於認證的共享鏈路金鑰,並且能夠建立彼此的加密連線。要連線意味著裝置當前共享RFCOMM通道,並且能夠彼此傳輸資料。在建立RFCOMM連線之前,目前的Android藍芽API需要配對裝置。(當您使用藍芽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意圖接收有關每個裝置發現BroadcastReceiver。對每一個裝置,系統將播出action_found意圖。這種意圖進行額外的領域extra_device和extra_class,包含一個藍芽裝置和藍芽類,分別。例如,在這裡的你如何登記辦理廣播裝置時發現的:

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

所有的需要的物件初始化一個連線藍芽裝置的MAC地址。在這個例子中,它是儲存為一個ArrayAdapter,顯示給使用者的一部分。MAC地址可以被提取為啟動連線。你可以瞭解更多關於建立在該段的連線對連線裝置。

注意:執行裝置發現是藍芽介面卡的重要過程,將消耗大量資源。找到裝置進行連線後,確保您cancelDiscovery()在嘗試連線之前始終停止發現 。此外,如果您已經持有與裝置的連線,則執行發現可以顯著減少連線可用的頻寬,因此在連線時不應執行發現。

實現可發現性
如果要將本地裝置發現到其他裝置,請startActivityForResult(Intent, int)通過 ACTION_REQUEST_DISCOVERABLEIntent操作呼叫。這將通過系統設定(不停止應用程式)發出啟用可發現模式的請求。預設情況下,裝置可以發現120秒。您可以通過新增EXTRA_DISCOVERABLE_DURATIONIntent 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所示。如果使用者響應“是”,則裝置將在指定的時間內被發現。然後,您的活動將收到對onActivityResult())回撥的呼叫,結果程式碼等於裝置可發現的持續時間。如果使用者響應“否”,或者發生錯誤,結果程式碼將會出現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地址啟動連線)。當伺服器和客戶端都BluetoothSocket在相同的RFCOMM通道上連線時,伺服器和客戶端被認為是相互連線 的。此時,每個裝置可以獲取輸入和輸出流,並且可以開始資料傳輸,這將在“ 管理連線 ”一節中討論。本節介紹如何啟動兩臺裝置之間的連線。

伺服器裝置和客戶端裝置都BluetoothSocket以不同的方式獲得所需的裝置。當接收到連線時,伺服器將收到該訊息。當客戶端向伺服器開啟RFCOMM通道時,客戶端將收到該訊息。

一種實現技術是將每個裝置自動準備為伺服器,以便每個裝置都有一個伺服器套接字開啟並監聽連線。那麼任一裝置都可以啟動與其他裝置的連線併成為客戶端。或者,一個裝置可以顯式地“主持”連線並按需開啟伺服器套接字,而另一個裝置可以簡單地啟動連線。

注意:如果兩個裝置之前沒有配對,則Android框架將在連線過程中自動向使用者顯示配對請求通知或對話方塊,如圖3所示。因此,當嘗試連線裝置時,您的應用程式不會需要關心裝置是否配對。您的RFCOMM連線嘗試將阻止,直到使用者成功配對,否則將失敗,如果使用者拒絕配對,或配對失敗或超時。

作為伺服器連線

當您要連線兩個裝置時,必須通過持續開啟來充當伺服器BluetoothServerSocket。伺服器套接字的目的是監聽傳入的連線請求,並且當被接受時,提供連線BluetoothSocket。當從… BluetoothSocket獲取時BluetoothServerSocket,BluetoothServerSocket可以(應該)被丟棄,除非你想接受更多的連線。

關於UUID
通用唯一識別符號(UUID)是用於唯一標識資訊的字串ID的標準化128位格式。UUID的觀點是,它足夠大,您可以選擇任意隨機,它不會發生衝突。在這種情況下,它用於唯一標識您的應用程式的藍芽服務。要獲得UUID與你的應用程式中使用,你可以在Web上使用的許多隨機UUID發電機中的一個,然後初始化UUID用fromString(String)。

以下是設定伺服器套接字並接受連線的基本步驟:
1. 獲得BluetoothServerSocket通過呼叫 listenUsingRfcommWithServiceRecord(String, UUID)。
該字串是您的服務的可標識名稱,系統將自動寫入裝置上的新服務發現協議(SDP)資料庫條目(名稱是任意的,可以僅僅是您的應用程式名稱)。UUID也包含在SDP條目中,並將作為與客戶端裝置的連線協議的基礎。也就是說,當客戶端嘗試與此裝置連線時,它將攜帶唯一標識要連線的服務的UUID。這些UUID必須匹配才能接受連線(在下一步中)。
2. 通過呼叫開始監聽連線請求 accept()。
這是一個阻塞呼叫。當連線被接受或發生異常時,它將返回。僅當遠端裝置傳送了一個與該偵聽伺服器套接字註冊的UUID相匹配的UUID的連線請求時才接受連線。當成功時,accept()將返回一個已連線BluetoothSocket。
3. 除非你想接受額外的連線,請打電話 close()。
這將釋放伺服器socket和它的所有資源,但並沒有關閉連線的BluetoothSocket一個已經被退回accept()。不像TCP / IP,RFCOMM只允許每個通道的一個連線的客戶端的時間,所以在大多數情況下是有意義的呼叫close()在BluetoothServerSocket接受連線的套接字之後。

該accept()電話不應該在主要活動UI執行緒,因為它是一個阻塞呼叫,並防止對應用程式的任何其他互動來執行。使用一個BluetoothServerSocket或BluetoothSocket一個由您的應用程式管理的新執行緒來完成所有工作通常是有意義的。要中止阻塞呼叫諸如accept(),呼叫close()在BluetoothServerSocket(或BluetoothSocket從另一個執行緒)和封端的呼叫將立即返回。注意所有方法在一個BluetoothServerSocket或者BluetoothSocket 是執行緒安全的。

example

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 一旦聽完傳入的連線,你應該通常關閉你。在這個例子中,close()一旦BluetoothSocket被獲取就被呼叫。您可能還希望在您的執行緒中提供一個公共方法,可以BluetoothSocket在需要停止在伺服器套接字上偵聽的情況下關閉私有的方法。

作為客戶端連線

為了啟動與遠端裝置(持有開啟伺服器套接字的裝置)的連線,您必須首先獲取BluetoothDevice表示遠端裝置的物件。(BluetoothDevice有關查詢裝置的上述部分將介紹以下部分)。然後,您必須使用它 BluetoothDevice來獲取BluetoothSocket並啟動連線。

這是基本的過程:

  1. 使用BluetoothDevice,BluetoothSocket通過呼叫得到一個createRfcommSocketToServiceRecord(UUID)。
    這將初始化一個BluetoothSocket將連線到的BluetoothDevice。在此處傳遞的UUID必須與伺服器裝置開啟BluetoothServerSocket(使用listenUsingRfcommWithServiceRecord(String, UUID))時使用的UUID相匹配 。使用相同的UUID只是將UUID字串硬編碼到應用程式中,然後從伺服器和客戶端程式碼引用它。
  2. 通過呼叫啟動連線connect()。
    在此呼叫之後,系統將在遠端裝置上執行SDP查詢,以匹配UUID。如果查詢成功並且遠端裝置接受連線,則它將共享在連線期間使用的RFCOMM通道並connect()返回。這種方法是一個阻塞呼叫。如果出於任何原因,連線失敗或connect()方法超時(約12秒鐘後),則會引發異常。
    因為connect()是一個阻塞呼叫,這個連線過程應該總是在與主活動執行緒分開的執行緒中執行。

注意:呼叫時,應始終確保裝置未執行裝置發現connect()。如果發現正在進行中,則連線嘗試將顯著減慢,並且更有可能失敗。

example

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。

example
下面是一個如何看待的這個例子:

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[])從流中返回位元組時,使用父類中的成員處理程式將資料傳送到主要活動。然後它返回並等待流中的更多位元組。

傳送傳出資料就像write()從主活動呼叫執行緒的方法一樣簡單, 並傳遞要傳送的位元組。該方法然後簡單地呼叫write(byte[])將資料傳送到遠端裝置。

執行緒的cancel()方法很重要,因此可以隨時關閉連線BluetoothSocket。當您完成使用藍芽連線時,應始終呼叫此操作。