首页 > 代码库 > Android蓝牙通信总结

Android蓝牙通信总结

这篇文章要达到的目标:
1.介绍在Android系统上实现蓝牙通信的过程中涉及到的概念。
2.在android系统上实现蓝牙通信的步骤。
3.在代码实现上的考虑。
4.例子代码实现(手持设备和蓝牙串口设备通信)。
 
1.介绍在Android系统上实现蓝牙通信的过程中使用到的类
BluetoothAdapter
Represents the local Bluetooth adapter (Bluetooth radio). The BluetoothAdapter is the entry-point for all Bluetooth interaction. Using this, you can discover other Bluetooth devices, query a list of bonded (paired) devices, instantiate a BluetoothDevice using a known MAC address, and create a BluetoothServerSocket to listen for communications from other devices.
BluetoothAdapter
      代表当前手机的蓝牙设备。
BluetoothDevice
Represents a remote Bluetooth device. Use this to request a connection with a remote device through a BluetoothSocket or query information about the device such as its name, address, class, and bonding state.
      代表要链接的蓝牙设备。通过这个类,用户就可以实现与两个蓝牙设备之间的链接(当前设备和被链接设备)。
BluetoothSocket
Represents the interface for a Bluetooth socket (similar to a TCP Socket). This is the connection point that allows an application to exchange data with another Bluetooth device via InputStream and OutputStream.
   蓝牙设备的通信中,某个服务,用一个唯一的UUID标识,比如串口通信服务(通过蓝牙实现串口通信)的UUID是:"00001101-0000-1000-8000-00805F9B34FB".
     下述代码做的事情是:1.通过BluetoothDevice提供的方法,实现当前设备与目标设备的连接建立。2.使用1中获得的BluetoothSocket,进行实际的连接建立。3.然后,通过BluetoothSocket获取InputStream和OutputStream,就可以与目标设备进行通信;通过InputStream读取目标设备传递过来的信息;通过OutputStream,将数据写到目标设备。
private class ConnectThread extends Thread {    private final BluetoothSocket mmSocket ;    private final BluetoothDevice mmDevice ;    public ConnectThread(BluetoothDevice device, boolean secure) {      mmDevice = device;      BluetoothSocket tmp = null ;      // Get a BluetoothSocket for a connection with the      // given BluetoothDevice      try {        tmp = device.createRfcommSocketToServiceRecord( MY_UUID );      } catch (IOException e) {        Log. e( TAG,e);      }      mmSocket = tmp;    }    public void run() {      // Always cancel discovery because it will slow down a connection      mAdapter.cancelDiscovery();      try {        mmSocket .connect();      } catch (IOException e) {        // Close the socket        try {          mmSocket .close();        } catch (IOException e2) {          Log. e( TAG,e2);        }        connectionFailed();        return ;      }      synchronized (BluetoothService. this ) {        mConnectThread = null ;      }      // Start the connected thread      connected(mmSocket);    }    public void cancel() {      try {        mmSocket .close();      } catch (IOException e) {        Log. e( TAG, "close() of connect " + mSocketType + " socket failed", e);      }    }  }
BluetoothServerSocket
Represents an open server socket that listens for incoming requests (similar to a TCP ServerSocket). In order to connect two Android devices, one device must open a server socket with this class. When a remote Bluetooth device makes a connection request to the this device, the BluetoothServerSocket will return a connected BluetoothSocket when the connection is accepted.
      在蓝牙通信建立的过程中,初始化时,结构是Server/Client。Server端创建一个BluetoothServerSocket,专门监听Client端发出的请求,并对监听到的请求进行接收。成功接收后,返回一个BluetoothSocket给Client端。Client端,使用BluetoothSocket与Server端进行通信。
      比如,手持设备和蓝牙串口通信设备通信,蓝牙串口设备是Server端,它监听来自Client端的链接请求。成功建立连接之后,Client端就可以与Server端进行通信。
      在Server/Client架构中,一定有一端是Server端。
      使用蓝牙进行双向通信时,则是连接的设备,既做Client端,又做Server端。
 
2.在android系统上实现蓝牙通信的步骤
 
1.setting up Bluetooth.
2.finding devices that are either paired or available in the local are.
3.connecting devices.
4.transferring data between devices.
 
 
3.代码实现上的考虑
 
 1) 首先检查设备的的蓝牙是否可用。onCreate()方法。
 2) 在蓝牙可用的基础下,检查蓝牙是否已经开启了;如果没有开启,则开启;如果开启了,则新建一个蓝牙通信服务。onStart()方法。
 3) 在蓝牙通信服务开启(和可用)的基础下,检查当前的服务状态,如果是没有建立任何连接的,则选择要链接的设备(寻找已经配对的设备;首先要在蓝牙程序中进行设备的配对),然后建立连接。onResume()方法。
 4) 接下来就可以使用蓝牙通信服务进行数据传递mService;蓝牙数据的接收,mService会通过Handler传递给UI线程。
 
其中:3),选择要链接的设备,在下面的代码中,是这样做的,从已经配对的蓝牙设备中选择名称是DeviceName的蓝牙设备,然后建立连接。所以,首先要在系统的蓝牙管理程序中,先与指定的设备进行配对,这样,程序才可以在已经配对的设备中找到名称是DeviceName的蓝牙设备。
如下图:
 
 

状态转换顺序:
onCreate()---->onStart(),如果没有启用,则进行启用----->onPause()---->onResume()
onCreate()---->onStart(),如果蓝牙已经启用了,则---->onResume().
 
public class MainActivity extends Activity implements View.OnClickListener {  private BluetoothAdapter mBluetoothAdapter = null;  private BluetoothService mService = null;  private BluetoothDevice mOnePairedDevice;  private final String DeviceName = "LILEI" ;  @Override  protected void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout. activity_main);    mBluetoothAdapter = BluetoothAdapter. getDefaultAdapter();    if ( mBluetoothAdapter == null ) {      Toast.makeText(this, "Bluetooth is not available", Toast.LENGTH_LONG ).show();      finish();    }    ....    }  @Override  public void onStart() {    super.onStart();    if (! mBluetoothAdapter.isEnabled()) {      // Enable Bluetooth function      Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE );      startActivityForResult(enableIntent, REQUEST_ENABLE_BT);    } else if (mService == null) {      setupBluetoothService();    }  }  @Override  public void onResume() {    super.onResume();    // Performing this check in onResume() covers the case in which BT was    // not enabled during onStart(), so we were paused to enable it...    // onResume() will be called when ACTION_REQUEST_ENABLE activity returns.    if ( mService != null ) {      // Only if the state is STATE_NONE, do we know that we haven‘t started already      if ( mService.getState() == BluetoothService.STATE_NONE) {        if (mOnePairedDevice == null) {          mOnePairedDevice = getASpecificPairedDevice(DeviceName );        }        if (mOnePairedDevice != null) {          mService.start(mOnePairedDevice );        } else {          Toast. makeText(this, "Not Found the paired Device." , Toast.LENGTH_LONG ).show();        }      }    }  }  @Override  public void onDestroy() {    super.onDestroy();    if ( mService != null ) {      mService.stop();    }  }  @Override  public void onActivityResult(int requestCode, int resultCode, Intent data) {    switch (requestCode) {      case REQUEST_ENABLE_BT:        // When the request to enable Bluetooth returns        if (resultCode == Activity.RESULT_OK) {          setupBluetoothService();        } else {          // User did not enable Bluetooth or an error occurred          Log. d(TAG, "BT not enabled");          Toast. makeText(this, R.string. bt_not_enabled_leaving, Toast.LENGTH_SHORT ).show();          finish();        }    }  }  public void setupBluetoothService() {    mService = new BluetoothService(mHandler );  }       private BluetoothDevice getASpecificPairedDevice(String name) {    Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();    // If there are paired devices    if (pairedDevices.size() > 0) {      // Loop through paired devices      for (BluetoothDevice device : pairedDevices) {        if (name.equals(device.getName())) {          return device;        }      }    }    return null;  }    ...   }
 
4.例子代码实现
  
     在确定了蓝牙可用,蓝牙开启,要链接的设备,这些信息之后,就可以建立连接,然后进行通信管理。
      下述代码的实现:
      1.当前服务的状态标识---有这种。根据状态标识执行不同的操作。
      2.ConnectedThread线程,会不断循环读取InputStream,即是读取连接设备传递过来的信息,若有信息,则通过Handler传递给UI线程;当要对连接设备传递信息的时候,调用ConnectedThread暴露的public方法,write( byte [] buffer),writeFileMessage( byte [] buffer),writeFileStream(InputStream input).传递信息给连接设备时,使用OutputStream。
   要关闭与连接设备的通信,则调用暴露方法cancle(),即是调用BluetoothSocket的close()方法。
    3.在初始化服务时,使用ConnectThread来建立与目标设备的链接,获取BluetoothSocket.因为BluetoothSocket的connect方法是阻塞的,所以在独立的线程中执行,避免阻塞UI线程。
     服务的状态变化:
     初始化时,STATE_NONE;执行建立连接的操作时,STATE_CONNECTING;当已经和链接设备建立连接了,即是获取了BluetoothSocket,则标注状态STATE_CONNECTED;
     停止连接线程,读写线程之后,则标注状态为STATE_NONE。
 
public class BluetoothService {    private static final String TAG = "BluetoothService" ;    private static final UUID MY_UUID = UUID.fromString( "00001101-0000-1000-8000-00805F9B34FB");    // Constants that indicate the current connection state    public static final int STATE_NONE = 0; // we‘re doing nothing    public static final int STATE_LISTEN = 1; // now listening for incoming connections    public static final int STATE_CONNECTING = 2; // now initiating an outgoing connection    public static final int STATE_CONNECTED = 3; // now connected to a remote device    private final BluetoothAdapter mAdapter ;    private Handler mHandler ;    private ConnectThread mConnectThread ;    private ConnectedThread mConnectedThread ;    private int mState ;    public BluetoothService(Handler handler) {        mAdapter = BluetoothAdapter.getDefaultAdapter();        mHandler = handler;        mState = STATE_NONE ;    }    public void setHandler(Handler handler) {        mHandler = handler;    }    public void start(BluetoothDevice remoteDevice) {        connect(remoteDevice, false );    }    public int getState() {        return mState ;    }    public void stop() {        Log. d( TAG, "stop" );        if (mConnectThread != null) {            mConnectThread .cancel();            mConnectThread = null ;        }        if (mConnectedThread != null) {            mConnectedThread .cancel();            mConnectedThread = null ;        }        setState( STATE_NONE );    }    /**     * This thread runs while attempting to make an outgoing connection with a device. It runs     * straight through; the connection either succeeds or fails.     */    private class ConnectThread extends Thread {        private final BluetoothSocket mmSocket;        private final BluetoothDevice mmDevice;        private String mSocketType ;        public ConnectThread(BluetoothDevice device, boolean secure) {            mmDevice = device;            BluetoothSocket tmp = null ;            mSocketType = secure ? "Secure" : "Insecure";            // Get a BluetoothSocket for a connection with the            // given BluetoothDevice            try {                tmp = device.createRfcommSocketToServiceRecord( MY_UUID );            } catch (IOException e) {                Log. e( TAG, "Socket Type: " + mSocketType + "create() failed", e);            }            mmSocket = tmp;        }        public void run() {            Log. i( TAG, "BEGIN mConnectThread SocketType:" + mSocketType );            setName( "ConnectThread" + mSocketType );            // Always cancel discovery because it will slow down a connection            mAdapter .cancelDiscovery();            // Make a connection to the BluetoothSocket            try {                // This is a blocking call and will only return on a                // successful connection or an exception                mmSocket .connect();            } catch (IOException e) {                // Close the socket                try {                    mmSocket .close();                } catch (IOException e2) {                    Log. e( TAG, "unable to close() " + mSocketType                            + " socket during connection failure" , e2);                }                connectionFailed();                return ;            }            // Reset the ConnectThread because we‘re done            synchronized (BluetoothService. this) {                mConnectThread = null ;            }            // Start the connected thread            connected( mmSocket , mmDevice , mSocketType );        }        public void cancel() {            try {                mmSocket .close();            } catch (IOException e) {                Log. e( TAG, "close() of connect " + mSocketType + " socket failed", e);            }        }    }    /**     * Indicate that the connection attempt failed and notify the UI Activity.     */    private void connectionFailed() {        // Send a failure message back to the Activity        Message msg = mHandler .obtainMessage(Constants. MESSAGE_TOAST);        Bundle bundle = new Bundle();        bundle.putString(Constants. TOAST , "Unable to connect device" );        msg.setData(bundle);        mHandler .sendMessage(msg);    }    /**     * Set the current state of the chat connection     *     * @param state An integer defining the current connection state     */    private synchronized void setState( int state) {        Log. d( TAG, "setState() " + mState + " -> " + state);        mState = state;        // Give the new state to the Handler so the UI Activity can update        mHandler .obtainMessage(Constants. MESSAGE_STATE_CHANGE, state, -1).sendToTarget();    }    /**     * Start the ConnectedThread to begin managing a Bluetooth connection     *     * @param socket The BluetoothSocket on which the connection was made     * @param device The BluetoothDevice that has been connected     */    public synchronized void connected(BluetoothSocket socket, BluetoothDevice device,            final String socketType) {        Log. d( TAG, "connected, Socket Type:" + socketType);        // Cancel the thread that completed the connection        if (mConnectThread != null) {            mConnectThread .cancel();            mConnectThread = null ;        }        // Cancel any thread currently running a connection        if (mConnectedThread != null) {            mConnectedThread .cancel();            mConnectedThread = null ;        }        // Start the thread to manage the connection and perform transmissions        mConnectedThread = new ConnectedThread(socket, socketType);        mConnectedThread .start();        // Send the name of the connected device back to the UI Activity        Message msg = mHandler .obtainMessage(Constants. MESSAGE_DEVICE_NAME);        Bundle bundle = new Bundle();        bundle.putString(Constants. DEVICE_NAME , device.getName());        msg.setData(bundle);        mHandler .sendMessage(msg);        setState( STATE_CONNECTED );    }    /**     * This thread runs during a connection with a remote device. It handles all incoming and     * outgoing transmissions.     */    private class ConnectedThread extends Thread {        private final BluetoothSocket mmSocket;        private final InputStream mmInStream;        private final OutputStream mmOutStream;        public ConnectedThread(BluetoothSocket socket, String socketType) {            Log. d( TAG, "create ConnectedThread: " + socketType);            mmSocket = socket;            InputStream tmpIn = null ;            OutputStream tmpOut = null ;            // Get the BluetoothSocket input and output streams            try {                tmpIn = socket.getInputStream();                tmpOut = socket.getOutputStream();            } catch (IOException e) {                Log. e( TAG, "temp sockets not created" , e);            }            mmInStream = tmpIn;            mmOutStream = tmpOut;        }        public void run() {            Log. i( TAG, "BEGIN mConnectedThread" );            byte [] buffer = new byte[3024];            int bytes;            // Keep listening to the InputStream while connected            while (true ) {                try {                    // Read from the InputStream                    bytes = mmInStream .read(buffer);                    // Send the obtained bytes to the UI Activity                    mHandler .obtainMessage(Constants. MESSAGE_READ, bytes, -1, buffer)                            .sendToTarget();                } catch (IOException e) {                    Log. e( TAG, "disconnected" , e);                    connectionLost();                    break ;                }            }        }        /**         * Write to the connected OutStream.         *         * @param buffer The bytes to write         */        public void write( byte[] buffer) {            try {                mmOutStream .write(buffer);                // Share the sent message back to the UI Activity                mHandler .obtainMessage(Constants. MESSAGE_WRITE, -1, -1, buffer).sendToTarget();            } catch (IOException e) {                Log. e( TAG, "Exception during write" , e);            }        }        public void writeFileMessage( byte[] buffer) {            try {                mmOutStream .write(buffer);            } catch (IOException e) {                Log. e( TAG, "Exception during write" , e);            }        }        public void writeFileStream (InputStream input) {            byte [] buffer = new byte[1024];            int readLengh;            try {                while ((readLengh = input.read(buffer)) != -1) {                    mmOutStream .write(buffer, 0, readLengh);                }                mHandler                        .obtainMessage(Constants. MESSAGE_WRITE , -1, -1,                                "Finish send file to remoteDevice." )                        .sendToTarget();            } catch (IOException e) {                Log. e( TAG, "Exception during write" , e);                mHandler .obtainMessage(Constants. MESSAGE_WRITE, -1, -1,                        "Error when send file to remoteDevice").sendToTarget();            }        }        public void cancel() {            try {                mmSocket .close();            } catch (IOException e) {                Log. e( TAG, "close() of connect socket failed" , e);            }        }    }    /**     * Indicate that the connection was lost and notify the UI Activity.     */    private void connectionLost() {        // Send a failure message back to the Activity        Message msg = mHandler .obtainMessage(Constants. MESSAGE_TOAST);        Bundle bundle = new Bundle();        bundle.putString(Constants. TOAST , "Device connection was lost" );        msg.setData(bundle);        mHandler .sendMessage(msg);        // Start the service over to restart listening mode    }    /**     * Start the ConnectThread to initiate a connection to a remote device.     *     * @param device The BluetoothDevice to connect     * @param secure Socket Security type - Secure (true) , Insecure (false)     */    public synchronized void connect(BluetoothDevice device, boolean secure) {        Log. d( TAG, "connect to: " + device);        // Cancel any thread attempting to make a connection        if (mState == STATE_CONNECTING) {            if (mConnectThread != null) {                mConnectThread .cancel();                mConnectThread = null ;            }        }        // Cancel any thread currently running a connection        if (mConnectedThread != null) {            mConnectedThread .cancel();            mConnectedThread = null ;        }        // Start the thread to connect with the given device        mConnectThread = new ConnectThread(device, secure);        mConnectThread .start();        setState( STATE_CONNECTING );    }    /**     * Write to the ConnectedThread in an unsynchronized manner     *     * @param out The bytes to write     * @see ConnectedThread#write(byte[])     */    public void write( byte[] out, boolean isFile) {        // Create temporary object        ConnectedThread r;        // Synchronize a copy of the ConnectedThread        synchronized (this ) {            if (mState != STATE_CONNECTED)                return ;            r = mConnectedThread ;        }        // Perform the write unsynchronized        if (isFile) {            r.writeFileMessage(out);        } else {            r.write(out);        }    }    public void write(InputStream in) {        // Create temporary object        ConnectedThread r;        // Synchronize a copy of the ConnectedThread        synchronized (this ) {            if (mState != STATE_CONNECTED)                return ;            r = mConnectedThread ;        }        // Perform the write unsynchronized        r. writeFileStream(in);    }}
参考资料:
http://developer.android.com/guide/topics/connectivity/bluetooth.html
http://blog.csdn.net/metalseed/article/details/7988945
 
    

Android蓝牙通信总结