分享

android蓝牙开发——二

 wanwanstudy 2012-02-08

为了在两台设备上创建一个连接,你必须在软件上实现服务器端和客户端的机制,因为一个设备必须必须打开一个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. }  
[java:firstline[1]] view plaincopy
  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. }} 

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多