简体   繁体   中英

InputStream not receiving data via Bluetooth

EDIT 1: MCVE-

I let my entire code be in the main question, but since I was asked for an MCVE-

The device is not receiving the data sent to it by the other connected Android device. Code is not running past "inputStream.read(buffer)" as it is getting no data to receive.

The code for sending data:

public void sendData(String s) throws IOException{
    byte[] byteString=s.getBytes();
    outputStream.write(byteString);
    outputStream.flush();
    Toast.makeText(getApplicationContext(), "Message sent", Toast.LENGTH_SHORT).show();
}

For receiving:

while (true){
            inputStream.read(buffer);
            final String str=new String(buffer);
            try{
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(context, str, Toast.LENGTH_SHORT).show();
                    }
                });
            }catch (Exception e){
                Toast.makeText(context, "Error in reading characters", Toast.LENGTH_SHORT).show();
            }
        }

Also, I have connected my socket this way:

Method method = bluetoothDevice.getClass().getMethod("createRfcommSocket", new Class[]{int.class});
bluetoothSocket = (BluetoothSocket) method.invoke(bluetoothDevice, 2);

Using Port Number 1 or the createRfCcommSocketToServiceRecord was not working as the connection was failing then.

THE ENTIRE PROBLEM:

I am working on an app where I need a feature to provide a 2 way communication between two android devices via Bluetooth. The data being sent will be simple strings.

I am able to connect two devices properly. Here is my code for that:

 bluetoothAdapter=BluetoothAdapter.getDefaultAdapter();
    if(!bluetoothAdapter.isEnabled()){
        Intent intent= new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
        startActivityForResult(intent,1);
    }

    pairedDevices=bluetoothAdapter.getBondedDevices();
    List<String> pairedDevicesList=new ArrayList<String>();
    if (pairedDevices.size() > 0) {
        for (BluetoothDevice device : pairedDevices) {
            pairedDevicesList.add(device.toString());
        }
    }

    listView.setVisibility(View.GONE);
    btListView.setVisibility(View.VISIBLE);
    btListView.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, pairedDevicesList));

This displays all paired devices on a ListView. Now upon selecting any of those devices, a connection is made with the following:

try{
        btListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                try {
                    BluetoothDevice pairedDevicesArray[] = pairedDevices.toArray(new BluetoothDevice[pairedDevices.size()]);
                    bluetoothDevice = pairedDevicesArray[position];
                } catch (Exception e) {
                    Toast.makeText(getApplicationContext(), "Error in getting BT Device", Toast.LENGTH_SHORT).show();
                }

                try {
                    ParcelUuid parcelUuidArray[];
                    List<UUID> uuidList = new ArrayList<UUID>();
                    Class cl = Class.forName("android.bluetooth.BluetoothDevice");
                    Class[] params = {};
                    Method method = cl.getMethod("getUuids", params);
                    Object[] args = {};
                    parcelUuidArray = (ParcelUuid[]) method.invoke(bluetoothDevice, args);
                    for (ParcelUuid u : parcelUuidArray) {
                        uuidList.add(u.getUuid());
                    }
                    uuid = uuidList.get(0);
                    Toast.makeText(getApplicationContext(), uuid.toString(), Toast.LENGTH_SHORT).show();
                } catch (Exception e) {
                    Toast.makeText(getApplicationContext(), "Error in getting UUIDs", Toast.LENGTH_SHORT).show();
                }


                try {
                    Method method = bluetoothDevice.getClass().getMethod("createRfcommSocket", new Class[]{int.class});
                    bluetoothSocket = (BluetoothSocket) method.invoke(bluetoothDevice, 2);
                    //bluetoothSocket = bluetoothDevice.createRfcommSocketToServiceRecord(uuid);
                    Toast.makeText(getApplicationContext(), bluetoothSocket.toString(), Toast.LENGTH_SHORT).show();
                } catch (Exception e) {
                    Toast.makeText(getApplicationContext(), "Error in getting BT Socket", Toast.LENGTH_SHORT).show();
                }

                try {
                    bluetoothAdapter.cancelDiscovery();
                    if (!bluetoothSocket.isConnected()) {
                        bluetoothSocket.connect();
                    }
                    Toast.makeText(getApplicationContext(), "CONNECTION SUCCESSFUL!", Toast.LENGTH_SHORT).show();
                } catch (Exception e) {
                    Toast.makeText(getApplicationContext(), "Error in connecting", Toast.LENGTH_SHORT).show();
                }
                btListView.setVisibility(View.GONE);
                listView.setVisibility(View.VISIBLE);

                try {
                    outputStream = bluetoothSocket.getOutputStream();
                    inputStream = bluetoothSocket.getInputStream();
                    Toast.makeText(getApplicationContext(), "Streams retrieved", Toast.LENGTH_SHORT).show();
                    //listenForData();
                } catch (Exception e) {
                    Toast.makeText(getApplicationContext(), "Error in getting streams", Toast.LENGTH_SHORT).show();
                }



                final Handler handler = new Handler();
                try {
                    BluetoothSocketListener bluetoothSocketListener = new BluetoothSocketListener(bluetoothSocket, handler, getApplicationContext());
                    Thread newThread = new Thread(bluetoothSocketListener);
                    newThread.start();
                    Toast.makeText(getApplicationContext(), "New Thread Running", Toast.LENGTH_SHORT).show();
                }catch(Exception e){
                    Toast.makeText(getApplicationContext(), "Error in new thread", Toast.LENGTH_SHORT).show();
                }

            }
        });
    }catch (Exception e){
        Toast.makeText(getApplicationContext(), "Error in 2nd listview", Toast.LENGTH_SHORT).show();
    }

The Handler at the end of this block of code creates another Thread which keeps running to receive the data sent by the other device. Here is the code for that Thread:

public class BluetoothSocketListener implements Runnable {

private BluetoothSocket bluetoothSocket;
private Handler handler;
Context context;
public BluetoothSocketListener(BluetoothSocket socket, Handler handler, Context c){
    this.bluetoothSocket=socket;
    this.handler=handler;
    this.context=c;
}

@Override
public void run(){
    int bufferSize=1024;
    final byte[] buffer=new byte[bufferSize];
    try {
        final InputStream inputStream = bluetoothSocket.getInputStream();
        int bytesRead = -1;
        String message = "";
        handler.post(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(context, "Listening for data with Stream: "+inputStream.toString(), Toast.LENGTH_SHORT).show();
            }
        });
        while (true){
            bytesRead= inputStream.read(buffer);
            final String str=new String(buffer);
            try{
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(context, str, Toast.LENGTH_SHORT).show();
                    }
                });
            }catch (Exception e){
                Toast.makeText(context, "Error in reading characters", Toast.LENGTH_SHORT).show();
            }
        }

    }catch(Exception e){
        handler.post(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(context, "Error in listening to data", Toast.LENGTH_SHORT).show();
            }
        });
    }
}}

And, the data is being written to the OutputStream on any device with this function in the MainActivity class:

public void sendData(String s) throws IOException{
    byte[] byteString=s.getBytes();
    outputStream.write(byteString);
    outputStream.flush();
    Toast.makeText(getApplicationContext(), "Message sent", Toast.LENGTH_SHORT).show();
}

The String s is the string that is needed to be sent.

Upon running my app on two separate device simultaneously (One device has Android 5.1.1 and the other has Android 6.0), the devices how up in the list of paired devices on both phones and the connections to each other is made.

All the toasts are displayed, including the "New Thread Running" and "Listening For Data" so the code does run fine. Upon trying to send anything, the "Message sent" toast is getting displayed too. But the data is not getting received (Code isn't running past inputStream.read(buffer) as a test Toast I had put after it didn't get displayed).

This means that it is going upto that point and waiting to read the data from InputSream that never comes, even if it is successfully written to the OutputStream on the other device.

Upon closing the app, the error message "Error in listening to data" is displayed proving that the 2nd Thread was running fine continuously throughout.

Can anyone please tell where I have gone wrong? Is it that my device is looking into an InputStream from one socket while the data is being sent to a different one by the 2nd device. If so, how am I supposed to make sure it listens at the right socket?

I looked through all similar questions on StackOverflow and have even checked the Bluetooth Chat App example by Google but couldn't find any solution to this.I have tried different ways of sending the string (using OutputStreamWriting and encoding it in UTF-8, etc) and receiving it (trying to receive data character by character, or using BufferedReader on the InputStream), but there was the exact same problem every time. The rest of the app works fine.

Thanks.

I apologize for the first post. This is an answer, if I understand Bluetooth correctly your run() function starts during the CONNECTING phase and before CONNECTED . The read function is starting on an incomplete connection.

@Override
public void run(){
    int bufferSize=1024;
    final byte[] buffer=new byte[bufferSize];
    try {
        final InputStream inputStream = bluetoothSocket.getInputStream();
        int bytesRead = -1;
        String message = "";
        handler.post(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(context, "Listening for data with Stream: "+inputStream.toString(), Toast.LENGTH_SHORT).show();
            }
        });
        while (true){
            bytesRead= inputStream.read(buffer);
            final String str=new String(buffer);

Specifically, addressing your reference to the Chat example and the similarities that you edited some of that code. The problem is partly due to your use of "Toast" instead of the Log.d API because it takes so long for graphics.

// 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

/** Constructor. Prepares a new BluetoothChat session.
 *
 * @param context The UI Activity Context
 * @param handler A Handler to send messages back to the UI Activity
 */
public BluetoothSPP(Context context, Handler handler) {
    mAdapter = BluetoothAdapter.getDefaultAdapter();
    mState = STATE_NONE;
    mHandler = handler;
}

/** Set the current state of the chat connection
 *
 * @param state An integer defining the current connection state
 */
private synchronized void setState(int state) {
    Log.v(TAG, "BT State changed " + 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();
}

Observe the message handler between mConnectedThread.start() and setState(STATE_CONNECTED)

public synchronized void connected(BluetoothSocket socket, BluetoothDevice
        device, final String socketType) {
    Log.d(TAG, "BT 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;
    }

    // Cancel the accept thread because we only want to connect to one device
    if (mSecureAcceptThread != null) {
        mSecureAcceptThread.cancel();
        mSecureAcceptThread = null;
    }
    if (mInsecureAcceptThread != null) {
        mInsecureAcceptThread.cancel();
        mInsecureAcceptThread = 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);
}

Here is the read thread.

private class ConnectedThread extends Thread {
    private final BluetoothSocket mmSocket;
    private final InputStream mmInStream;
    private final OutputStream mmOutStream;

    public ConnectedThread(BluetoothSocket socket, String socketType) {
        Log.v(TAG, "BT Connected: " + 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, "BT sockets not created", e);
        }

        mmInStream = tmpIn;
        mmOutStream = tmpOut;
    }

    public void run() {
        Log.i(TAG, "BEGIN BT Monitor");
        byte[] buffer = new byte[1024];
        int bytes;

        // Keep listening to the InputStream while connected
        while (mState == STATE_CONNECTED) {
            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, "BT Monitor Disconnected", e);
                connectionLost();
                // Start the service over to restart listening mode
                BluetoothSPP.this.start();
                break;
            }
        }
        Log.i(TAG, "End BT Monitor");

    }

The output in the Android Studio logcat looked like this. It never entered the while (mState == STATE_CONNECTED) loop.

Output of Android Studio logcat during code execution

I corrected the code by adding a loop that handled the connecting state.

public void run() {
        Log.i(TAG, "BEGIN BT Monitor");
        byte[] buffer = new byte[1024];
        int bytes;

        while (mState == STATE_CONNECTING){
            Log.i(TAG, "BT Monitor Paused");
        }
        // Keep listening to the InputStream while connected
        while (mState == STATE_CONNECTED) {
            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, "BT Monitor Disconnected", e);
                connectionLost();
                // Start the service over to restart listening mode
                BluetoothSPP.this.start();
                break;
            }
        }
        Log.i(TAG, "End BT Monitor");

    }

The logcat output shows the "STATE_CONNECTING" loop occurring 15 times before the Log message stating the connection had been completed and once after (showing the time dependency) of Toast and other messages on code execution.

Android Studio logcat output showing code execution

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