• 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
     
        
  • 相关阅读:
    python每日一题:使用套接字创建分布式进程
    市盈率分析
    python每日一题:分布式进程之坑点
    python每日一题:比较单线程,多线程,协程的运行效率
    python每日一题:锁知识点
    python每日一题:查找一篇文档中的人名和城市
    python之装饰器@
    python每日一题:利用字典实现超市购物程序
    【Java基础】多线程
    【Java基础】异常处理
  • 原文地址:https://www.cnblogs.com/ttylinux/p/4167135.html
Copyright © 2020-2023  润新知