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
相關文章
相關標籤/搜索