android藍牙開發——二

 

爲了在兩臺設備上創建一個連接,你必須在軟件上實現服務器端和客戶端的機制,因爲一個設備必須必須打開一個server socket,而另一個必須初始化這個連接(使用服務器端設備的MAC地址進行初始化)。

當服務器端和客戶端在同一個RFCOMM信道上都有一個BluetoothSocket時,就可以認爲它們之間建立了一個連接。在這個時刻,每個設備能獲得一個輸出流和一個輸入流,也能夠開始數據傳輸。本節介紹如何在兩個設備之間初始化一個連接。

服務器端和客戶端獲得BluetoothSocket的方法是不同的,服務器端是當一個進入的連接被接受時才產生一個BluetoothSocket,客戶端是在打開一個到服務器端的RFCOMM信道時獲得BluetoothSocket的。

遠程設備請求配對

 Figure 3: The Bluetooth pairing dialog.

 

 一種實現技術是,每一個設備都自動作爲一個服務器,所以每個設備都有一個server socket並監聽連接。然後每個設備都能作爲客戶端建立一個到另一臺設備的連接。另外一種代替方法是,一個設備按需打開一個server socket,另外一個設備僅初始化一個到這個設備的連接。

 

Note: 如果兩個設備在建立連接之前並沒有配對,那麼在建立連接的過程中,Android框架將自動顯示一個配對請求的notification或者一個對話框,如Figure 3所示。所以,在嘗試連接設備時,你的應用程序無需確保設備之間已經進行了配對。你的RFCOMM連接將會在用戶確認配對之後繼續進行,或者用戶拒絕或者超時之後失敗。

作爲一個服務器進行連接

當你想要連接兩個設備時,其中一個必須保持一個打開的BluetoothServerSocket,作爲服務器。服務器socket將監聽進入的連接請求,一旦連接被接受,將產生一個BluetoothSocket。

  1. 調用listenUsingRfcommWithServiceRecord(String, UUID)獲得一個BluetoothServerSocket

    字符串參數是你的服務的標識名,系統將自動將這個標識名寫到設備上一個新的Service Discovery Protocol(SDP)數據庫條目中(這個標識名可以簡單地作爲你的程序的名字,也就是說可以把你的程序的名字作爲命名)。UUID也會被包含在這個新的SDP條目中,並作爲與客戶端設備建立連接的基礎。 That is, when the client attempts to connect with this device, it will carry a UUID that uniquely identifies the service with which it wants to connect. These UUIDs must match in order for the connection to be accepted (in the next step).

  2. 調用accept()開始監聽連接請求

    這是一個阻塞調用。當一個連接被接受或者發生異常時將返回。一個連接,僅當一個遠程設備發出的請求包含UUID匹配正在監聽的server socket所註冊的UUID時被接受。成功的時候,accept()將返回一個連接的BluetoothSocket。

  3. 除非你要接受另外一個連接,否則調用close,關閉server socket。

    這將釋放server socket和它佔用的所有資源,但不要用來關閉accept返回的已連接的BluetoothSocket。不像TCP/IP,RFCOMM僅允許一個信道在某一時刻有一個連接的客戶端。所以,創建了一個連接的socket之後立即調用close()來關閉BluetoothServerSocket。

accept()方法不應該在主Activity UI線程中執行,因爲它是一個阻塞調用,如果在主Activity UI線程中條也能夠將會阻止與用戶的交互。一般使用BluetoothServerSocket或者BluetoothSocket進行相關工作時都是在一個新的線程中。爲了避免調用諸如accept()這樣的阻塞調用,針對來自其他線程的BluetoothServerSocket或者BluetoothSocket調用close()將會使阻塞調用立即返回。注意,針對BluetoothServerSocket或者BluetoothSocket調用的方法都是線程安全的,也就是說可以在多個線程中使用。

示例

  1. private class AcceptThread extends Thread {  
  2.     private final BluetoothServerSocket mmServerSocket;  
  3.   
  4.     public AcceptThread() {  
  5.         // Use a temporary object that is later assigned to mmServerSocket,   
  6.         // because mmServerSocket is final   
  7.         BluetoothServerSocket tmp = null;  
  8.         try {  
  9.             // MY_UUID is the app's UUID string, also used by the client code   
  10.             tmp = mAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID);  
  11.         } catch (IOException e) { }  
  12.         mmServerSocket = tmp;  
  13.     }  
  14.   
  15.     public void run() {  
  16.         BluetoothSocket socket = null;  
  17.         // Keep listening until exception occurs or a socket is returned   
  18.         while (true) {  
  19.             try {  
  20.                 socket = mmServerSocket.accept();  
  21.             } catch (IOException e) {  
  22.                 break;  
  23.             }  
  24.             // If a connection was accepted   
  25.             if (socket != null) {  
  26.                 // Do work to manage the connection (in a separate thread)   
  27.                 manageConnectedSocket(socket);  
  28.                 mmServerSocket.close();  
  29.                 break;  
  30.             }  
  31.         }  
  32.     }  
  33.   
  34.     /** Will cancel the listening socket, and cause the thread to finish */  
  35.     public void cancel() {  
  36.         try {  
  37.             mmServerSocket.close();  
  38.         } catch (IOException e) { }  
  39.     }  
  40. }  

 

 

manageConnectedSocket() 這個方法能初始化用於傳輸數據的線程。

 

感覺這些英語基本上都能看懂,就不翻譯了...

Connecting as a client

In order to initiate a connection with a remote device (a device holding an open server socket), you must first obtain a BluetoothDevice object that represents the remote device. (Getting a BluetoothDevice is covered in the above section about Finding Devices.) You must then use the BluetoothDevice to acquire a BluetoothSocket and initiate the connection.

Here's the basic procedure:

  1. Using the BluetoothDevice, get a BluetoothSocket by calling createRfcommSocketToServiceRecord(UUID).

    This initializes a BluetoothSocket that will connect to the BluetoothDevice. The UUID passed here must match the UUID used by the server device when it opened its BluetoothServerSocket (with listenUsingRfcommWithServiceRecord(String, UUID)). Using the same UUID is simply a matter of hard-coding the UUID string into your application and then referencing it from both the server and client code.

  2. Initiate the connection by calling connect().

    Upon this call, the system will perform an SDP lookup on the remote device in order to match the UUID. If the lookup is successful and the remote device accepts the connection, it will share the RFCOMM channel to use during the connection and connect() will return. This method is a blocking call. If, for any reason, the connection fails or the connect() method times out (after about 12 seconds), then it will throw an exception.

    Because connect() is a blocking call, this connection procedure should always be performed in a thread separate from the main Activity thread.

    Note: You should always ensure that the device is not performing device discovery when you call connect(). If discovery is in progress, then the connection attempt will be significantly slowed and is more likely to fail.

Example

Here is a basic example of a thread that initiates a Bluetooth connection:

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
        mAdapter.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) { }
    }
}

Notice that cancelDiscovery() is called before the connection is made. You should always do this before connecting and it is safe to call without actually checking whether it is running or not (but if you do want to check, call isDiscovering()).

manageConnectedSocket() is a fictional method in the application that will initiate the thread for transferring data, which is discussed in the section about Managing a Connection.

When you're done with your BluetoothSocket, always call close() to clean up. Doing so will immediately close the connected socket and clean up all internal resources.

 
 

當你成功地連接了兩臺(或多臺)設備時,每個設備都有一個已連接的BluetoothSocket。這時你可以在設備之間共享數據,樂趣纔剛開始。 使用BluetoothSocket,傳輸二進制數據的過程是簡單的:

  1. 分別通過getInputStream()和getOutputStream()獲得管理數據傳輸的InputStream和OutputStream。
  2. 通過read(byte[])和write(byte[])從流中讀取或寫入數據。

 首先,你必須使用一個線程專門用於數據的讀或寫。這是非常重要的,因爲read(byte[])和write(byte[])方法都是阻塞調用。read(byte[])將會阻塞到流中有數據可讀。write(byte[])一般不會阻塞,但當遠程設備的中間緩衝區已滿而對方沒有及時地調用read(byte[])時將會一直阻塞。所以,你的線程中的主循環將一直用於從InputStream中讀取數據。 A separate public method in the thread can be used to initiate writes to the OutputStream.

Example

  1. private class ConnectedThread extends Thread {      
  2. private final BluetoothSocket mmSocket;      
  3. private final InputStream mmInStream;      
  4. private final OutputStream mmOutStream;     
  5.  public ConnectedThread(BluetoothSocket socket) {          
  6. mmSocket = socket;          
  7. InputStream tmpIn = null;          
  8. OutputStream tmpOut = null;          
  9. // Get the input and output streams, using temp objects because           
  10. // member streams are final           
  11. try {              
  12. tmpIn = socket.getInputStream();              
  13. tmpOut = socket.getOutputStream();          
  14. catch (IOException e) { }          
  15. mmInStream = tmpIn;          
  16. mmOutStream = tmpOut;      
  17. }      
  18. public void run() {          
  19. byte[] buffer = new byte[1024];  // buffer store for the stream           
  20. int bytes; // bytes returned from read()           
  21. // Keep listening to the InputStream until an exception occurs           
  22. while (true) {              
  23. try {                  
  24. // Read from the InputStream                   
  25. bytes = mmInStream.read(buffer);                  
  26. // Send the obtained bytes to the UI Activity                   
  27. mHandler.obtainMessage(MESSAGE_READ, bytes, -1, buffer).sendToTarget();              
  28. catch (IOException e) {                  
  29. break;              
  30. }          
  31. }      
  32. }      
  33. /* Call this from the main Activity to send data to the remote device */      
  34. public void write(byte[] bytes) {          
  35. try {              
  36. mmOutStream.write(bytes);          
  37. catch (IOException e) { }      
  38. }      
  39. /* Call this from the main Activity to shutdown the connection */      
  40. public void cancel() {          
  41. try {              
  42. mmSocket.close();          
  43. catch (IOException e) { }      
  44. }}  

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章