简体   繁体   中英

Android: Bluetooth - How to read incoming data

I have successfully paired and connected with a Bluetooth device. I am now interested in receiving all data being transferred between the 2 and seeing whats what.

I am getting the input stream from the socket and attempting to read it. I return this and just log it.

The only way I know of doing this from what I have read is just do read with a byte buffer to return an int. However I should have loads of data coming through. How can I continually read out data being transferred, and also format as bytes rather than an int.

Thanks.

Full code below:

public class ConnectThread {

    private BluetoothSocketWrapper bluetoothSocket;
    private BluetoothDevice device;
    private boolean secure;
    private BluetoothAdapter adapter;
    private List<UUID> uuidCandidates;
    private int candidate;


    /**
     * @param device the device
     * @param secure if connection should be done via a secure socket
     * @param adapter the Android BT adapter
     * @param uuidCandidates a list of UUIDs. if null or empty, the Serial PP id is used
     */
    public ConnectThread(BluetoothDevice device, boolean secure, BluetoothAdapter adapter,
                              List<UUID> uuidCandidates) {
        this.device = device;
        this.secure = secure;
        this.adapter = adapter;
        this.uuidCandidates = uuidCandidates;

        if (this.uuidCandidates == null || this.uuidCandidates.isEmpty()) {
            this.uuidCandidates = new ArrayList<UUID>();
            this.uuidCandidates.add(UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"));
        }
    }

    public BluetoothSocketWrapper connect() throws IOException {
        boolean success = false;
        while (selectSocket()) {
            adapter.cancelDiscovery();

            try {
                bluetoothSocket.connect();
                success = true;
                break;
            } catch (IOException e) {
                //try the fallback
                try {
                    bluetoothSocket = new FallbackBluetoothSocket(bluetoothSocket.getUnderlyingSocket());
                    Thread.sleep(500);
                    bluetoothSocket.connect();
                    success = true;
                    break;
                } catch (FallbackException e1) {
                    Log.w("BT", "Could not initialize FallbackBluetoothSocket classes.", e);
                } catch (InterruptedException e1) {
                    Log.w("BT", e1.getMessage(), e1);
                } catch (IOException e1) {
                    Log.w("BT", "Fallback failed. Cancelling.", e1);
                }
            }
        }

        if (!success) {
            throw new IOException("Could not connect to device: "+ device.getAddress());
        }

        receiveData(bluetoothSocket);
        return bluetoothSocket;
    }

    private boolean selectSocket() throws IOException {
        if (candidate >= uuidCandidates.size()) {
            return false;
        }

        BluetoothSocket tmp;
        UUID uuid = uuidCandidates.get(candidate++);

        Log.i("BT", "Attempting to connect to Protocol: "+ uuid);
        if (secure) {
            tmp = device.createRfcommSocketToServiceRecord(uuid);
        } else {
            tmp = device.createInsecureRfcommSocketToServiceRecord(uuid);
        }
        bluetoothSocket = new NativeBluetoothSocket(tmp);

        return true;
    }

    public static interface BluetoothSocketWrapper {

        InputStream getInputStream() throws IOException;

        OutputStream getOutputStream() throws IOException;

        String getRemoteDeviceName();

        void connect() throws IOException;

        String getRemoteDeviceAddress();

        void close() throws IOException;

        BluetoothSocket getUnderlyingSocket();

    }


    public static class NativeBluetoothSocket implements BluetoothSocketWrapper {

        private BluetoothSocket socket;

        public NativeBluetoothSocket(BluetoothSocket tmp) {
            this.socket = tmp;
        }

        @Override
        public InputStream getInputStream() throws IOException {
            return socket.getInputStream();
        }

        @Override
        public OutputStream getOutputStream() throws IOException {
            return socket.getOutputStream();
        }

        @Override
        public String getRemoteDeviceName() {
            return socket.getRemoteDevice().getName();
        }

        @Override
        public void connect() throws IOException {
            socket.connect();
        }

        @Override
        public String getRemoteDeviceAddress() {
            return socket.getRemoteDevice().getAddress();
        }

        @Override
        public void close() throws IOException {
            socket.close();
        }

        @Override
        public BluetoothSocket getUnderlyingSocket() {
            return socket;
        }

    }

    public class FallbackBluetoothSocket extends NativeBluetoothSocket {

        private BluetoothSocket fallbackSocket;

        public FallbackBluetoothSocket(BluetoothSocket tmp) throws FallbackException {
            super(tmp);
            try
            {
                Class<?> clazz = tmp.getRemoteDevice().getClass();
                Class<?>[] paramTypes = new Class<?>[] {Integer.TYPE};
                Method m = clazz.getMethod("createRfcommSocket", paramTypes);
                Object[] params = new Object[] {Integer.valueOf(1)};
                fallbackSocket = (BluetoothSocket) m.invoke(tmp.getRemoteDevice(), params);
            }
            catch (Exception e)
            {
                throw new FallbackException(e);
            }
        }

        @Override
        public InputStream getInputStream() throws IOException {
            return fallbackSocket.getInputStream();
        }

        @Override
        public OutputStream getOutputStream() throws IOException {
            return fallbackSocket.getOutputStream();
        }


        @Override
        public void connect() throws IOException {
            fallbackSocket.connect();
        }


        @Override
        public void close() throws IOException {
            fallbackSocket.close();
        }

    }

    public static class FallbackException extends Exception {

        /**
         *
         */
        private static final long serialVersionUID = 1L;

        public FallbackException(Exception e) {
            super(e);
        }

    }

    public void sendData(BluetoothSocketWrapper socket, int data) throws IOException{
        ByteArrayOutputStream output = new ByteArrayOutputStream(4);
        output.write(data);
        OutputStream outputStream = socket.getOutputStream();
        outputStream.write(output.toByteArray());
    }

    public int receiveData(BluetoothSocketWrapper socket) throws IOException{
        byte[] buffer = new byte[256];
        ByteArrayInputStream input = new ByteArrayInputStream(buffer);
        InputStream inputStream = socket.getInputStream();
        inputStream.read(buffer);
        return input.read();
    }
}

In the first place, stop using ByteArrayInputStream and ByteArrayOutputStream for more control.

If the socket sends/receives text, do this.

TO SEND :

String text = "My message";
socketOutputStream.write(text.getBytes());

TO RECEIVE :

int length = socketInputStream.read(buffer);
String text = new String(buffer, 0, length);

The socketOutputStream should be your bluetoothSocket.getOutputStream() .

If the socket sends/receives large loads of data, the key is the while loop to prevent out of memory exceptions. The data will be read by chunks of (for example every 4KB of buffer size), when you choose the buffer size, consider the heap size, if you're live-streaming media, consider latency and quality too.

TO SEND:

int length;
while ((length = largeDataInputStream.read(buffer)) != -1) {
    socketOutputStream.write(buffer, 0, length);
}

TO RECEIVE:

int length;
//socketInputStream never returns -1 unless connection is broken
while ((length = socketInputStream.read(buffer)) != -1) {
    largeDataOutputStream.write(buffer, 0, length);
    if (progress >= dataSize) {
        break; //Break loop if progress reaches the limit
    }
}

FAQ:

  • How do I get the size of receiving data? You'll have to make your own implementation to notify remote device to get ready to receive data (including file size), this will require at least a dual-socket connection (2 sockets, 1 device), for example, 1 socket for text fragments and custom commands, and 1 socket for large data, like files or streaming.
  • What are largeDataInputStream and largeDataOutputStream ? These streams can be normal I/O streams, FileInputStream / FileOutputStream or etc.
  • Why the while loop for BluetoothSocket never finishes? The socket input is continuously receiving data, and the read() methods blocks itself until data is detected. To prevent blocking the code in that line, while loop must be broken.

NOTE: This answer could need an edit. I'm not a native English speaker.

Following the above advice, I am now using this code to retrieve data.

    public void receiveData(BluetoothSocketWrapper socket) throws IOException{
    InputStream socketInputStream =  socket.getInputStream();
    byte[] buffer = new byte[256];
    int bytes;

    // Keep looping to listen for received messages
    while (true) {
        try {
            bytes = socketInputStream.read(buffer);            //read bytes from input buffer
            String readMessage = new String(buffer, 0, bytes);
            // Send the obtained bytes to the UI Activity via handler
            Log.i("logging", readMessage + "");
        } catch (IOException e) {
            break;
        }
    }

}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM